snowbridge_pallet_ethereum_client/
impls.rs1use super::*;
4use frame_support::ensure;
5use snowbridge_beacon_primitives::ExecutionProof;
6
7use snowbridge_beacon_primitives::{
8 merkle_proof::{generalized_index_length, subtree_index},
9 receipt::verify_receipt_proof,
10};
11use snowbridge_ethereum::Log as AlloyLog;
12use snowbridge_verification_primitives::{
13 VerificationError::{self, *},
14 Verifier, *,
15};
16
17impl<T: Config> Verifier for Pallet<T> {
18 fn verify(event_log: &Log, proof: &Proof) -> Result<(), VerificationError> {
24 Self::verify_execution_proof(&proof.execution_proof)
25 .map_err(|e| InvalidExecutionProof(e.into()))?;
26
27 Self::verify_receipt_inclusion(
28 proof.execution_proof.execution_header.receipts_root(),
29 &proof.receipt_proof.1,
30 event_log,
31 )?;
32
33 Ok(())
34 }
35}
36
37impl<T: Config> Pallet<T> {
38 pub fn verify_receipt_inclusion(
41 receipts_root: H256,
42 receipt_proof: &[Vec<u8>],
43 log: &Log,
44 ) -> Result<(), VerificationError> {
45 let receipt = verify_receipt_proof(receipts_root, receipt_proof).ok_or(InvalidProof)?;
46 if !receipt.logs().iter().any(|l| Self::check_log_match(log, l)) {
47 tracing::error!(
48 target: "ethereum-client",
49 "💫 Event log not found in receipt for transaction",
50 );
51 return Err(LogNotFound)
52 }
53 Ok(())
54 }
55
56 fn check_log_match(log: &Log, receipt_log: &AlloyLog) -> bool {
57 let equal = receipt_log.data.data.0 == log.data &&
58 receipt_log.address.0 == log.address.0 &&
59 receipt_log.topics().len() == log.topics.len();
60 if !equal {
61 return false
62 }
63 for (_, (topic1, topic2)) in receipt_log.topics().iter().zip(log.topics.iter()).enumerate()
64 {
65 if topic1.0 != topic2.0 {
66 return false
67 }
68 }
69 true
70 }
71
72 pub(crate) fn verify_execution_proof(execution_proof: &ExecutionProof) -> DispatchResult {
76 let latest_finalized_state =
77 FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get())
78 .ok_or(Error::<T>::NotBootstrapped)?;
79 ensure!(
81 execution_proof.header.slot <= latest_finalized_state.slot,
82 Error::<T>::HeaderNotFinalized
83 );
84
85 let beacon_block_root: H256 = execution_proof
86 .header
87 .hash_tree_root()
88 .map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;
89
90 match &execution_proof.ancestry_proof {
91 Some(proof) => {
92 Self::verify_ancestry_proof(
93 beacon_block_root,
94 execution_proof.header.slot,
95 &proof.header_branch,
96 proof.finalized_block_root,
97 )?;
98 },
99 None => {
100 let state = <FinalizedBeaconState<T>>::get(beacon_block_root)
104 .ok_or(Error::<T>::ExpectedFinalizedHeaderNotStored)?;
105 if execution_proof.header.slot != state.slot {
106 return Err(Error::<T>::ExpectedFinalizedHeaderNotStored.into())
107 }
108 },
109 }
110
111 let execution_header_root: H256 = execution_proof
115 .execution_header
116 .hash_tree_root()
117 .map_err(|_| Error::<T>::BlockBodyHashTreeRootFailed)?;
118
119 let execution_header_gindex = Self::execution_header_gindex();
120 ensure!(
121 verify_merkle_branch(
122 execution_header_root,
123 &execution_proof.execution_branch,
124 subtree_index(execution_header_gindex),
125 generalized_index_length(execution_header_gindex),
126 execution_proof.header.body_root
127 ),
128 Error::<T>::InvalidExecutionHeaderProof
129 );
130 Ok(())
131 }
132
133 fn verify_ancestry_proof(
137 block_root: H256,
138 block_slot: u64,
139 block_root_proof: &[H256],
140 finalized_block_root: H256,
141 ) -> DispatchResult {
142 let state = <FinalizedBeaconState<T>>::get(finalized_block_root)
143 .ok_or(Error::<T>::ExpectedFinalizedHeaderNotStored)?;
144
145 ensure!(block_slot < state.slot, Error::<T>::HeaderNotFinalized);
146
147 let index_in_array = block_slot % (SLOTS_PER_HISTORICAL_ROOT as u64);
148 let leaf_index = (SLOTS_PER_HISTORICAL_ROOT as u64) + index_in_array;
149
150 ensure!(
151 verify_merkle_branch(
152 block_root,
153 block_root_proof,
154 leaf_index as usize,
155 config::BLOCK_ROOT_AT_INDEX_DEPTH,
156 state.block_roots_root
157 ),
158 Error::<T>::InvalidAncestryMerkleProof
159 );
160
161 Ok(())
162 }
163}