1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
3use super::*;
4use frame_support::ensure;
5use snowbridge_beacon_primitives::ExecutionProof;
67use snowbridge_beacon_primitives::merkle_proof::{generalized_index_length, subtree_index};
8use snowbridge_ethereum::Receipt;
9use snowbridge_verification_primitives::{
10 VerificationError::{self, *},
11 Verifier, *,
12};
1314impl<T: Config> Verifier for Pallet<T> {
15/// Verify a message by verifying the existence of the corresponding
16 /// Ethereum log in a block. Returns the log if successful. The execution header containing
17 /// the log is sent with the message. The beacon header containing the execution header
18 /// is also sent with the message, to check if the header is an ancestor of a finalized
19 /// header.
20fn verify(event_log: &Log, proof: &Proof) -> Result<(), VerificationError> {
21Self::verify_execution_proof(&proof.execution_proof)
22 .map_err(|e| InvalidExecutionProof(e.into()))?;
2324let receipt = Self::verify_receipt_inclusion(
25 proof.execution_proof.execution_header.receipts_root(),
26&proof.receipt_proof.1,
27 )?;
2829 event_log.validate().map_err(|_| InvalidLog)?;
3031// Convert snowbridge_core::inbound::Log to snowbridge_ethereum::Log.
32let event_log = snowbridge_ethereum::Log {
33 address: event_log.address,
34 topics: event_log.topics.clone(),
35 data: event_log.data.clone(),
36 };
3738if !receipt.contains_log(&event_log) {
39log::error!(
40 target: "ethereum-client",
41"💫 Event log not found in receipt for transaction",
42 );
43return Err(LogNotFound)
44 }
4546Ok(())
47 }
48}
4950impl<T: Config> Pallet<T> {
51/// Verifies that the receipt encoded in `proof.data` is included in the block given by
52 /// `proof.block_hash`.
53pub fn verify_receipt_inclusion(
54 receipts_root: H256,
55 receipt_proof: &[Vec<u8>],
56 ) -> Result<Receipt, VerificationError> {
57let result = verify_receipt_proof(receipts_root, receipt_proof).ok_or(InvalidProof)?;
5859match result {
60Ok(receipt) => Ok(receipt),
61Err(err) => {
62log::trace!(
63 target: "ethereum-client",
64"💫 Failed to decode transaction receipt: {}",
65 err
66 );
67Err(InvalidProof)
68 },
69 }
70 }
7172/// Validates an execution header with ancestry_proof against a finalized checkpoint on
73 /// chain.The beacon header containing the execution header is sent, plus the execution header,
74 /// along with a proof that the execution header is rooted in the beacon header body.
75pub(crate) fn verify_execution_proof(execution_proof: &ExecutionProof) -> DispatchResult {
76let latest_finalized_state =
77 FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get())
78 .ok_or(Error::<T>::NotBootstrapped)?;
79// Checks that the header is an ancestor of a finalized header, using slot number.
80ensure!(
81 execution_proof.header.slot <= latest_finalized_state.slot,
82 Error::<T>::HeaderNotFinalized
83 );
8485let beacon_block_root: H256 = execution_proof
86 .header
87 .hash_tree_root()
88 .map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;
8990match &execution_proof.ancestry_proof {
91Some(proof) => {
92Self::verify_ancestry_proof(
93 beacon_block_root,
94 execution_proof.header.slot,
95&proof.header_branch,
96 proof.finalized_block_root,
97 )?;
98 },
99None => {
100// If the ancestry proof is not provided, we expect this beacon header to be a
101 // finalized beacon header. We need to check that the header hash matches the
102 // finalized header root at the expected slot.
103let state = <FinalizedBeaconState<T>>::get(beacon_block_root)
104 .ok_or(Error::<T>::ExpectedFinalizedHeaderNotStored)?;
105if execution_proof.header.slot != state.slot {
106return Err(Error::<T>::ExpectedFinalizedHeaderNotStored.into())
107 }
108 },
109 }
110111// Gets the hash tree root of the execution header, in preparation for the execution
112 // header proof (used to check that the execution header is rooted in the beacon
113 // header body.
114let execution_header_root: H256 = execution_proof
115 .execution_header
116 .hash_tree_root()
117 .map_err(|_| Error::<T>::BlockBodyHashTreeRootFailed)?;
118119let execution_header_gindex = Self::execution_header_gindex();
120ensure!(
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 );
130Ok(())
131 }
132133/// Verify that `block_root` is an ancestor of `finalized_block_root` Used to prove that
134 /// an execution header is an ancestor of a finalized header (i.e. the blocks are
135 /// on the same chain).
136fn verify_ancestry_proof(
137 block_root: H256,
138 block_slot: u64,
139 block_root_proof: &[H256],
140 finalized_block_root: H256,
141 ) -> DispatchResult {
142let state = <FinalizedBeaconState<T>>::get(finalized_block_root)
143 .ok_or(Error::<T>::ExpectedFinalizedHeaderNotStored)?;
144145ensure!(block_slot < state.slot, Error::<T>::HeaderNotFinalized);
146147let index_in_array = block_slot % (SLOTS_PER_HISTORICAL_ROOT as u64);
148let leaf_index = (SLOTS_PER_HISTORICAL_ROOT as u64) + index_in_array;
149150ensure!(
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 );
160161Ok(())
162 }
163}