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 fraud_proof_runtime_interface, DomainInherentExtrinsic, DomainInherentExtrinsicData,
11 DomainStorageKeyRequest, StatelessDomainRuntimeCall,
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::storage::StorageKey;
19use sp_core::H256;
20use sp_domains::extrinsics::deduplicate_and_shuffle_extrinsics;
21use sp_domains::proof_provider_and_verifier::StorageProofVerifier;
22use sp_domains::valued_trie::valued_ordered_trie_root;
23use sp_domains::{
24 BlockFees, BundleValidity, DomainId, ExecutionReceipt, ExtrinsicDigest, HeaderHashFor,
25 HeaderHashingFor, HeaderNumberFor, InboxedBundle, InvalidBundleType, RuntimeId, Transfers,
26 INITIAL_DOMAIN_TX_RANGE,
27};
28use sp_runtime::generic::Digest;
29use sp_runtime::traits::{
30 Block as BlockT, Hash, Header as HeaderT, NumberFor, UniqueSaturatedInto,
31};
32use sp_runtime::{OpaqueExtrinsic, SaturatedConversion};
33use sp_subspace_mmr::{ConsensusChainMmrLeafProof, MmrProofVerifier};
34use sp_trie::{LayoutV1, StorageProof};
35use subspace_core_primitives::U256;
36use trie_db::node::Value;
37
38pub fn verify_invalid_domain_extrinsics_root_fraud_proof<
40 CBlock,
41 Balance,
42 DomainHeader,
43 Hashing,
44 SKP,
45>(
46 bad_receipt: ExecutionReceipt<
47 NumberFor<CBlock>,
48 CBlock::Hash,
49 HeaderNumberFor<DomainHeader>,
50 HeaderHashFor<DomainHeader>,
51 Balance,
52 >,
53 fraud_proof: &InvalidExtrinsicsRootProof,
54 domain_id: DomainId,
55 runtime_id: RuntimeId,
56 state_root: CBlock::Hash,
57 domain_runtime_code: Vec<u8>,
58) -> Result<(), VerificationError<DomainHeader::Hash>>
59where
60 CBlock: BlockT,
61 DomainHeader: HeaderT,
62 DomainHeader::Hash: Into<H256> + PartialEq + Copy,
63 Hashing: Hasher<Out = CBlock::Hash>,
64 SKP: FraudProofStorageKeyProvider<NumberFor<CBlock>>,
65{
66 let InvalidExtrinsicsRootProof {
67 valid_bundle_digests,
68 invalid_inherent_extrinsic_proofs,
69 maybe_domain_runtime_upgraded_proof,
70 domain_chain_allowlist_proof,
71 domain_sudo_call_proof,
72 evm_domain_contract_creation_allowed_by_call_proof,
73 } = fraud_proof;
74
75 let invalid_inherent_extrinsic_data =
76 <InvalidInherentExtrinsicDataProof as BasicStorageProof<CBlock>>::verify::<SKP>(
77 invalid_inherent_extrinsic_proofs.clone(),
78 (),
79 &state_root,
80 )?;
81
82 let maybe_domain_runtime_upgrade =
83 maybe_domain_runtime_upgraded_proof.verify::<CBlock, SKP>(runtime_id, &state_root)?;
84
85 let domain_chain_allowlist = <DomainChainsAllowlistUpdateStorageProof as BasicStorageProof<
86 CBlock,
87 >>::verify::<SKP>(
88 domain_chain_allowlist_proof.clone(), domain_id, &state_root
89 )?;
90
91 let domain_sudo_call = <DomainSudoCallStorageProof as BasicStorageProof<CBlock>>::verify::<SKP>(
92 domain_sudo_call_proof.clone(),
93 domain_id,
94 &state_root,
95 )?;
96
97 let evm_domain_contract_creation_allowed_by_call =
98 <EvmDomainContractCreationAllowedByCallStorageProof as BasicStorageProof<CBlock>>::verify::<
99 SKP,
100 >(
101 evm_domain_contract_creation_allowed_by_call_proof.clone(),
102 domain_id,
103 &state_root,
104 )?;
105
106 let shuffling_seed = invalid_inherent_extrinsic_data.extrinsics_shuffling_seed;
107
108 let domain_inherent_extrinsic_data = DomainInherentExtrinsicData {
109 timestamp: invalid_inherent_extrinsic_data.timestamp,
110 maybe_domain_runtime_upgrade,
111 consensus_transaction_byte_fee: invalid_inherent_extrinsic_data
112 .consensus_transaction_byte_fee,
113 domain_chain_allowlist,
114 maybe_sudo_runtime_call: domain_sudo_call.maybe_call,
115 maybe_evm_domain_contract_creation_allowed_by_call:
116 evm_domain_contract_creation_allowed_by_call.maybe_call,
117 };
118
119 let DomainInherentExtrinsic {
120 domain_timestamp_extrinsic,
121 maybe_domain_chain_allowlist_extrinsic,
122 consensus_chain_byte_fee_extrinsic,
123 maybe_domain_set_code_extrinsic,
124 maybe_domain_sudo_call_extrinsic,
125 maybe_evm_domain_contract_creation_allowed_by_call_extrinsic,
126 } = fraud_proof_runtime_interface::construct_domain_inherent_extrinsic(
127 domain_runtime_code,
128 domain_inherent_extrinsic_data,
129 )
130 .ok_or(VerificationError::FailedToDeriveDomainInherentExtrinsic)?;
131
132 let bad_receipt_valid_bundle_digests = bad_receipt.valid_bundle_digests();
133 if valid_bundle_digests.len() != bad_receipt_valid_bundle_digests.len() {
134 return Err(VerificationError::InvalidBundleDigest);
135 }
136
137 let mut bundle_extrinsics_digests = Vec::new();
138 for (bad_receipt_valid_bundle_digest, bundle_digest) in bad_receipt_valid_bundle_digests
139 .into_iter()
140 .zip(valid_bundle_digests)
141 {
142 let bundle_digest_hash =
143 HeaderHashingFor::<DomainHeader>::hash_of(&bundle_digest.bundle_digest);
144 if bundle_digest_hash != bad_receipt_valid_bundle_digest {
145 return Err(VerificationError::InvalidBundleDigest);
146 }
147
148 bundle_extrinsics_digests.extend(bundle_digest.bundle_digest.clone());
149 }
150
151 let mut ordered_extrinsics =
152 deduplicate_and_shuffle_extrinsics(bundle_extrinsics_digests, shuffling_seed);
153
154 if let Some(domain_sudo_call_extrinsic) = maybe_domain_sudo_call_extrinsic {
169 let domain_sudo_call_extrinsic = ExtrinsicDigest::new::<
170 LayoutV1<HeaderHashingFor<DomainHeader>>,
171 >(domain_sudo_call_extrinsic);
172 ordered_extrinsics.push_front(domain_sudo_call_extrinsic);
173 }
174
175 let transaction_byte_fee_extrinsic = ExtrinsicDigest::new::<
176 LayoutV1<HeaderHashingFor<DomainHeader>>,
177 >(consensus_chain_byte_fee_extrinsic);
178 ordered_extrinsics.push_front(transaction_byte_fee_extrinsic);
179
180 if let Some(evm_domain_contract_creation_allowed_by_call_extrinsic) =
181 maybe_evm_domain_contract_creation_allowed_by_call_extrinsic
182 {
183 let evm_domain_contract_creation_allowed_by_call_extrinsic =
184 ExtrinsicDigest::new::<LayoutV1<HeaderHashingFor<DomainHeader>>>(
185 evm_domain_contract_creation_allowed_by_call_extrinsic,
186 );
187 ordered_extrinsics.push_front(evm_domain_contract_creation_allowed_by_call_extrinsic);
188 }
189
190 if let Some(domain_chain_allowlist_extrinsic) = maybe_domain_chain_allowlist_extrinsic {
191 let domain_chain_allowlist_extrinsic = ExtrinsicDigest::new::<
192 LayoutV1<HeaderHashingFor<DomainHeader>>,
193 >(domain_chain_allowlist_extrinsic);
194 ordered_extrinsics.push_front(domain_chain_allowlist_extrinsic);
195 }
196
197 if let Some(domain_set_code_extrinsic) = maybe_domain_set_code_extrinsic {
198 let domain_set_code_extrinsic = ExtrinsicDigest::new::<
199 LayoutV1<HeaderHashingFor<DomainHeader>>,
200 >(domain_set_code_extrinsic);
201 ordered_extrinsics.push_front(domain_set_code_extrinsic);
202 }
203
204 let timestamp_extrinsic = ExtrinsicDigest::new::<LayoutV1<HeaderHashingFor<DomainHeader>>>(
205 domain_timestamp_extrinsic,
206 );
207 ordered_extrinsics.push_front(timestamp_extrinsic);
208
209 let ordered_trie_node_values = ordered_extrinsics
210 .iter()
211 .map(|ext_digest| match ext_digest {
212 ExtrinsicDigest::Data(data) => Value::Inline(data),
213 ExtrinsicDigest::Hash(hash) => Value::Node(hash.0.as_slice()),
214 })
215 .collect();
216
217 let extrinsics_root = valued_ordered_trie_root::<LayoutV1<HeaderHashingFor<DomainHeader>>>(
218 ordered_trie_node_values,
219 );
220 if bad_receipt.domain_block_extrinsic_root == extrinsics_root {
221 return Err(VerificationError::InvalidProof);
222 }
223
224 Ok(())
225}
226
227pub fn verify_valid_bundle_fraud_proof<CBlock, DomainHeader, Balance, SKP>(
229 bad_receipt: ExecutionReceipt<
230 NumberFor<CBlock>,
231 CBlock::Hash,
232 HeaderNumberFor<DomainHeader>,
233 HeaderHashFor<DomainHeader>,
234 Balance,
235 >,
236 fraud_proof: &ValidBundleProof<NumberFor<CBlock>, CBlock::Hash, DomainHeader>,
237 domain_id: DomainId,
238 state_root: CBlock::Hash,
239 domain_runtime_code: Vec<u8>,
240) -> Result<(), VerificationError<DomainHeader::Hash>>
241where
242 CBlock: BlockT,
243 CBlock::Hash: Into<H256>,
244 DomainHeader: HeaderT,
245 DomainHeader::Hash: Into<H256> + PartialEq + Copy,
246 SKP: FraudProofStorageKeyProvider<NumberFor<CBlock>>,
247{
248 let ValidBundleProof {
249 bundle_with_proof, ..
250 } = fraud_proof;
251
252 bundle_with_proof.verify::<CBlock, SKP>(domain_id, &state_root)?;
253 let OpaqueBundleWithProof {
254 bundle,
255 bundle_index,
256 ..
257 } = bundle_with_proof;
258
259 let valid_bundle_digest = fraud_proof_runtime_interface::derive_bundle_digest(
260 domain_runtime_code,
261 bundle.extrinsics.clone(),
262 )
263 .ok_or(VerificationError::FailedToDeriveBundleDigest)?;
264
265 let bad_valid_bundle_digest = bad_receipt
266 .valid_bundle_digest_at(*bundle_index as usize)
267 .ok_or(VerificationError::TargetValidBundleNotFound)?;
268
269 if bad_valid_bundle_digest.into() == valid_bundle_digest {
270 Err(VerificationError::InvalidProof)
271 } else {
272 Ok(())
273 }
274}
275
276pub fn verify_invalid_state_transition_fraud_proof<CBlock, DomainHeader, Balance>(
278 bad_receipt: ExecutionReceipt<
279 NumberFor<CBlock>,
280 CBlock::Hash,
281 DomainHeader::Number,
282 DomainHeader::Hash,
283 Balance,
284 >,
285 bad_receipt_parent: ExecutionReceipt<
286 NumberFor<CBlock>,
287 CBlock::Hash,
288 DomainHeader::Number,
289 DomainHeader::Hash,
290 Balance,
291 >,
292 fraud_proof: &InvalidStateTransitionProof,
293 domain_runtime_code: Vec<u8>,
294) -> Result<(), VerificationError<DomainHeader::Hash>>
295where
296 CBlock: BlockT,
297 CBlock::Hash: Into<H256>,
298 DomainHeader: HeaderT,
299 DomainHeader::Hash: Into<H256> + From<H256>,
300 DomainHeader::Number: UniqueSaturatedInto<BlockNumber> + From<BlockNumber>,
301{
302 let InvalidStateTransitionProof {
303 execution_proof,
304 execution_phase,
305 ..
306 } = fraud_proof;
307
308 let (pre_state_root, post_state_root) = execution_phase
309 .pre_post_state_root::<CBlock, DomainHeader, Balance>(&bad_receipt, &bad_receipt_parent)?;
310
311 let call_data = execution_phase
312 .call_data::<CBlock, DomainHeader, Balance>(&bad_receipt, &bad_receipt_parent)?;
313
314 let execution_result = fraud_proof_runtime_interface::execution_proof_check(
315 (
316 bad_receipt_parent.domain_block_number.saturated_into(),
317 bad_receipt_parent.domain_block_hash.into(),
318 ),
319 pre_state_root,
320 execution_proof.encode(),
321 execution_phase.execution_method(),
322 call_data.as_ref(),
323 domain_runtime_code,
324 )
325 .ok_or(VerificationError::BadExecutionProof)?;
326
327 let valid_post_state_root = execution_phase
328 .decode_execution_result::<DomainHeader>(execution_result)?
329 .into();
330
331 let is_mismatch = valid_post_state_root != post_state_root;
332
333 let is_valid = is_mismatch == execution_phase.is_state_root_mismatch();
337
338 if is_valid {
339 Ok(())
340 } else {
341 Err(VerificationError::InvalidProof)
342 }
343}
344
345pub fn verify_invalid_domain_block_hash_fraud_proof<CBlock, Balance, DomainHeader>(
347 bad_receipt: ExecutionReceipt<
348 NumberFor<CBlock>,
349 CBlock::Hash,
350 DomainHeader::Number,
351 DomainHeader::Hash,
352 Balance,
353 >,
354 digest_storage_proof: StorageProof,
355 parent_domain_block_hash: DomainHeader::Hash,
356) -> Result<(), VerificationError<DomainHeader::Hash>>
357where
358 CBlock: BlockT,
359 Balance: PartialEq + Decode,
360 DomainHeader: HeaderT,
361{
362 let state_root = bad_receipt.final_state_root;
363 let digest_storage_key = StorageKey(sp_domains::system_digest_final_key());
364
365 let digest = StorageProofVerifier::<DomainHeader::Hashing>::get_decoded_value::<Digest>(
366 &state_root,
367 digest_storage_proof,
368 digest_storage_key,
369 )
370 .map_err(|err| {
371 VerificationError::StorageProof(storage_proof::VerificationError::DigestStorageProof(err))
372 })?;
373
374 let derived_domain_block_hash = sp_domains::derive_domain_block_hash::<DomainHeader>(
375 bad_receipt.domain_block_number,
376 bad_receipt.domain_block_extrinsic_root,
377 state_root,
378 parent_domain_block_hash,
379 digest,
380 );
381
382 if bad_receipt.domain_block_hash == derived_domain_block_hash {
383 return Err(VerificationError::InvalidProof);
384 }
385
386 Ok(())
387}
388
389pub fn verify_invalid_block_fees_fraud_proof<
391 CBlock,
392 DomainNumber,
393 DomainHash,
394 Balance,
395 DomainHashing,
396>(
397 bad_receipt: ExecutionReceipt<
398 NumberFor<CBlock>,
399 CBlock::Hash,
400 DomainNumber,
401 DomainHash,
402 Balance,
403 >,
404 storage_proof: &StorageProof,
405 domain_runtime_code: Vec<u8>,
406) -> Result<(), VerificationError<DomainHash>>
407where
408 CBlock: BlockT,
409 Balance: PartialEq + Decode,
410 DomainHashing: Hasher<Out = DomainHash>,
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 CBlock::Hash: Into<H256>,
459 Balance: PartialEq + Decode,
460 DomainHashing: Hasher<Out = DomainHash>,
461{
462 let storage_key = fraud_proof_runtime_interface::domain_storage_key(
463 domain_runtime_code,
464 DomainStorageKeyRequest::Transfers,
465 )
466 .ok_or(VerificationError::FailedToGetDomainStorageKey)?;
467
468 let transfers = StorageProofVerifier::<DomainHashing>::get_decoded_value::<Transfers<Balance>>(
469 &bad_receipt.final_state_root,
470 storage_proof.clone(),
471 StorageKey(storage_key),
472 )
473 .map_err(|err| {
474 VerificationError::StorageProof(storage_proof::VerificationError::TransfersStorageProof(
475 err,
476 ))
477 })?;
478
479 if bad_receipt.transfers == transfers {
481 return Err(VerificationError::InvalidProof);
482 }
483
484 Ok(())
485}
486
487fn check_expected_bundle_entry<CBlock, DomainHeader, Balance>(
491 bad_receipt: &ExecutionReceipt<
492 NumberFor<CBlock>,
493 CBlock::Hash,
494 HeaderNumberFor<DomainHeader>,
495 HeaderHashFor<DomainHeader>,
496 Balance,
497 >,
498 bundle_index: u32,
499 invalid_bundle_type: InvalidBundleType,
500 is_good_invalid_fraud_proof: bool,
501) -> Result<InboxedBundle<HeaderHashFor<DomainHeader>>, VerificationError<DomainHeader::Hash>>
502where
503 CBlock: BlockT,
504 DomainHeader: HeaderT,
505{
506 let targeted_invalid_bundle_entry = bad_receipt
507 .inboxed_bundles
508 .get(bundle_index as usize)
509 .ok_or(VerificationError::BundleNotFound)?;
510
511 let is_expected = if !is_good_invalid_fraud_proof {
512 targeted_invalid_bundle_entry.bundle == BundleValidity::Invalid(invalid_bundle_type.clone())
516 } else {
517 match &targeted_invalid_bundle_entry.bundle {
519 BundleValidity::Valid(_) => true,
522 BundleValidity::Invalid(invalid_type) => {
523 invalid_bundle_type.checking_order() < invalid_type.checking_order()
526 }
527 }
528 };
529
530 if !is_expected {
531 return Err(VerificationError::UnexpectedTargetedBundleEntry {
532 bundle_index,
533 fraud_proof_invalid_type_of_proof: invalid_bundle_type,
534 targeted_entry_bundle: targeted_invalid_bundle_entry.bundle.clone(),
535 });
536 }
537
538 Ok(targeted_invalid_bundle_entry.clone())
539}
540
541fn get_extrinsic_from_proof<DomainHeader: HeaderT>(
542 extrinsic_index: u32,
543 extrinsics_root: <HeaderHashingFor<DomainHeader> as Hasher>::Out,
544 proof_data: StorageProof,
545) -> Result<OpaqueExtrinsic, VerificationError<DomainHeader::Hash>> {
546 let storage_key =
547 StorageProofVerifier::<HeaderHashingFor<DomainHeader>>::enumerated_storage_key(
548 extrinsic_index,
549 );
550 StorageProofVerifier::<HeaderHashingFor<DomainHeader>>::get_decoded_value(
551 &extrinsics_root,
552 proof_data,
553 storage_key,
554 )
555 .map_err(|err| {
556 VerificationError::StorageProof(storage_proof::VerificationError::ExtrinsicStorageProof(
557 err,
558 ))
559 })
560}
561
562pub fn verify_invalid_bundles_fraud_proof<CBlock, DomainHeader, MmrHash, Balance, SKP, MPV>(
563 bad_receipt: ExecutionReceipt<
564 NumberFor<CBlock>,
565 CBlock::Hash,
566 HeaderNumberFor<DomainHeader>,
567 HeaderHashFor<DomainHeader>,
568 Balance,
569 >,
570 bad_receipt_parent: ExecutionReceipt<
571 NumberFor<CBlock>,
572 CBlock::Hash,
573 HeaderNumberFor<DomainHeader>,
574 HeaderHashFor<DomainHeader>,
575 Balance,
576 >,
577 invalid_bundles_fraud_proof: &InvalidBundlesProof<
578 NumberFor<CBlock>,
579 CBlock::Hash,
580 MmrHash,
581 DomainHeader,
582 >,
583 domain_id: DomainId,
584 state_root: CBlock::Hash,
585 domain_runtime_code: Vec<u8>,
586) -> Result<(), VerificationError<DomainHeader::Hash>>
587where
588 CBlock: BlockT,
589 DomainHeader: HeaderT,
590 CBlock::Hash: Into<H256>,
591 DomainHeader::Hash: Into<H256>,
592 MmrHash: Decode + Clone,
593 SKP: FraudProofStorageKeyProvider<NumberFor<CBlock>>,
594 MPV: MmrProofVerifier<MmrHash, NumberFor<CBlock>, CBlock::Hash>,
595{
596 let InvalidBundlesProof {
597 bundle_index,
598 invalid_bundle_type,
599 is_good_invalid_fraud_proof,
600 proof_data,
601 ..
602 } = invalid_bundles_fraud_proof;
603 let (bundle_index, is_good_invalid_fraud_proof) = (*bundle_index, *is_good_invalid_fraud_proof);
604
605 let targeted_invalid_bundle_entry = check_expected_bundle_entry::<CBlock, DomainHeader, Balance>(
606 &bad_receipt,
607 bundle_index,
608 invalid_bundle_type.clone(),
609 is_good_invalid_fraud_proof,
610 )?;
611 let bundle_extrinsic_root = targeted_invalid_bundle_entry.extrinsics_root;
612
613 match proof_data {
615 InvalidBundlesProofData::Bundle(bundle_with_proof)
616 | InvalidBundlesProofData::BundleAndExecution {
617 bundle_with_proof, ..
618 } => {
619 if bundle_with_proof.bundle_index != bundle_index {
620 return Err(VerificationError::UnexpectedInvalidBundleProofData);
621 }
622 bundle_with_proof.verify::<CBlock, SKP>(domain_id, &state_root)?;
623 }
624 InvalidBundlesProofData::Extrinsic(_) => {}
625 InvalidBundlesProofData::InvalidXDMProofData { .. } => {}
626 }
627
628 if let Some(invalid_extrinsic_index) = targeted_invalid_bundle_entry.invalid_extrinsic_index() {
631 if let InvalidBundlesProofData::Bundle(bundle_with_proof) = proof_data {
632 if bundle_with_proof.bundle.extrinsics.len() as u32 <= invalid_extrinsic_index {
633 return Ok(());
634 }
635 }
636 }
637
638 match &invalid_bundle_type {
639 InvalidBundleType::OutOfRangeTx(extrinsic_index) => {
640 let bundle = match proof_data {
641 InvalidBundlesProofData::Bundle(bundle_with_proof) => {
642 bundle_with_proof.bundle.clone()
643 }
644 _ => return Err(VerificationError::UnexpectedInvalidBundleProofData),
645 };
646
647 let opaque_extrinsic = bundle
648 .extrinsics
649 .get(*extrinsic_index as usize)
650 .cloned()
651 .ok_or(VerificationError::ExtrinsicNotFound)?;
652
653 let domain_tx_range = U256::MAX / INITIAL_DOMAIN_TX_RANGE;
654 let bundle_vrf_hash =
655 U256::from_be_bytes(*bundle.sealed_header.header.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 (mut 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 .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.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}