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