Skip to content
This repository was archived by the owner on Aug 15, 2025. It is now read-only.

Commit 4c850e8

Browse files
yrongvgeddes
andauthored
Inline execution update into proof of inbound message & Remove execution header storage (#125)
* Remove execution header storage & submit inbound message with execution update * Fix ci breaking * More refactoring * Update beacon test fixtures * Format code * Initialize storage with finalized checkpoint for benchmark * Update weights include verify the execution proof * Fix breaking test * Remove fixture not in use * Add detail for InvalidExecutionProof * Initialize checkpoint for tests on demand * Revert error as NotBootstrapped for submit_update_with_missing_bootstrap * Add verify_execution_header tests back * Add error test back * Cleanup templates * Update bridges/snowbridge/primitives/beacon/src/types.rs Co-authored-by: Vincent Geddes <vincent@snowfork.com> * Narrowed inputs * Remove fields irrelevant from proof * Update bridges/snowbridge/primitives/core/src/inbound.rs Co-authored-by: Vincent Geddes <vincent@snowfork.com> * Update bridges/snowbridge/pallets/ethereum-client/src/impls.rs Co-authored-by: Vincent Geddes <vincent@snowfork.com> * Polish --------- Co-authored-by: Vincent Geddes <vincent@snowfork.com>
1 parent 8be52c9 commit 4c850e8

36 files changed

Lines changed: 1224 additions & 1196 deletions

File tree

bridges/snowbridge/pallets/ethereum-client/fixtures/src/lib.rs

Lines changed: 180 additions & 94 deletions
Large diffs are not rendered by default.

bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -65,24 +65,6 @@ mod benchmarks {
6565
Ok(())
6666
}
6767

68-
#[benchmark]
69-
fn submit_execution_header() -> Result<(), BenchmarkError> {
70-
let caller: T::AccountId = whitelisted_caller();
71-
let checkpoint_update = make_checkpoint();
72-
let finalized_header_update = make_finalized_header_update();
73-
let execution_header_update = make_execution_header_update();
74-
let execution_header_hash = execution_header_update.execution_header.block_hash();
75-
EthereumBeaconClient::<T>::process_checkpoint_update(&checkpoint_update)?;
76-
EthereumBeaconClient::<T>::process_update(&finalized_header_update)?;
77-
78-
#[extrinsic_call]
79-
_(RawOrigin::Signed(caller.clone()), Box::new(*execution_header_update));
80-
81-
assert!(<ExecutionHeaders<T>>::contains_key(execution_header_hash));
82-
83-
Ok(())
84-
}
85-
8668
#[benchmark(extra)]
8769
fn bls_fast_aggregate_verify_pre_aggregated() -> Result<(), BenchmarkError> {
8870
EthereumBeaconClient::<T>::process_checkpoint_update(&make_checkpoint())?;
Lines changed: 103 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// SPDX-License-Identifier: Apache-2.0
22
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
33
use super::*;
4+
use frame_support::ensure;
5+
use primitives::ExecutionProof;
46

57
use snowbridge_core::inbound::{
68
VerificationError::{self, *},
@@ -14,32 +16,13 @@ impl<T: Config> Verifier for Pallet<T> {
1416
/// the log should be in the beacon client storage, meaning it has been verified and is an
1517
/// ancestor of a finalized beacon block.
1618
fn verify(event_log: &Log, proof: &Proof) -> Result<(), VerificationError> {
17-
log::info!(
18-
target: "ethereum-client",
19-
"💫 Verifying message with block hash {}",
20-
proof.block_hash,
21-
);
19+
Self::verify_execution_proof(&proof.execution_proof)
20+
.map_err(|e| InvalidExecutionProof(e.into()))?;
2221

23-
let header = <ExecutionHeaderBuffer<T>>::get(proof.block_hash).ok_or(HeaderNotFound)?;
24-
25-
let receipt = match Self::verify_receipt_inclusion(header.receipts_root, proof) {
26-
Ok(receipt) => receipt,
27-
Err(err) => {
28-
log::error!(
29-
target: "ethereum-client",
30-
"💫 Verification of receipt inclusion failed for block {}: {:?}",
31-
proof.block_hash,
32-
err
33-
);
34-
return Err(err)
35-
},
36-
};
37-
38-
log::trace!(
39-
target: "ethereum-client",
40-
"💫 Verified receipt inclusion for transaction at index {} in block {}",
41-
proof.tx_index, proof.block_hash,
42-
);
22+
let receipt = Self::verify_receipt_inclusion(
23+
proof.execution_proof.execution_header.receipts_root(),
24+
&proof.receipt_proof.1,
25+
)?;
4326

4427
event_log.validate().map_err(|_| InvalidLog)?;
4528

@@ -53,18 +36,11 @@ impl<T: Config> Verifier for Pallet<T> {
5336
if !receipt.contains_log(&event_log) {
5437
log::error!(
5538
target: "ethereum-client",
56-
"💫 Event log not found in receipt for transaction at index {} in block {}",
57-
proof.tx_index, proof.block_hash,
39+
"💫 Event log not found in receipt for transaction",
5840
);
5941
return Err(LogNotFound)
6042
}
6143

62-
log::info!(
63-
target: "ethereum-client",
64-
"💫 Receipt verification successful for {}",
65-
proof.block_hash,
66-
);
67-
6844
Ok(())
6945
}
7046
}
@@ -74,9 +50,9 @@ impl<T: Config> Pallet<T> {
7450
/// `proof.block_hash`.
7551
pub fn verify_receipt_inclusion(
7652
receipts_root: H256,
77-
proof: &Proof,
53+
receipt_proof: &[Vec<u8>],
7854
) -> Result<Receipt, VerificationError> {
79-
let result = verify_receipt_proof(receipts_root, &proof.data.1).ok_or(InvalidProof)?;
55+
let result = verify_receipt_proof(receipts_root, receipt_proof).ok_or(InvalidProof)?;
8056

8157
match result {
8258
Ok(receipt) => Ok(receipt),
@@ -90,4 +66,96 @@ impl<T: Config> Pallet<T> {
9066
},
9167
}
9268
}
69+
70+
/// Validates an execution header with ancestry_proof against a finalized checkpoint on
71+
/// chain.The beacon header containing the execution header is sent, plus the execution header,
72+
/// along with a proof that the execution header is rooted in the beacon header body.
73+
pub(crate) fn verify_execution_proof(execution_proof: &ExecutionProof) -> DispatchResult {
74+
let latest_finalized_state =
75+
FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get())
76+
.ok_or(Error::<T>::NotBootstrapped)?;
77+
// Checks that the header is an ancestor of a finalized header, using slot number.
78+
ensure!(
79+
execution_proof.header.slot <= latest_finalized_state.slot,
80+
Error::<T>::HeaderNotFinalized
81+
);
82+
83+
// Gets the hash tree root of the execution header, in preparation for the execution
84+
// header proof (used to check that the execution header is rooted in the beacon
85+
// header body.
86+
let execution_header_root: H256 = execution_proof
87+
.execution_header
88+
.hash_tree_root()
89+
.map_err(|_| Error::<T>::BlockBodyHashTreeRootFailed)?;
90+
91+
ensure!(
92+
verify_merkle_branch(
93+
execution_header_root,
94+
&execution_proof.execution_branch,
95+
config::EXECUTION_HEADER_SUBTREE_INDEX,
96+
config::EXECUTION_HEADER_DEPTH,
97+
execution_proof.header.body_root
98+
),
99+
Error::<T>::InvalidExecutionHeaderProof
100+
);
101+
102+
let beacon_block_root: H256 = execution_proof
103+
.header
104+
.hash_tree_root()
105+
.map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;
106+
107+
match &execution_proof.ancestry_proof {
108+
Some(proof) => {
109+
Self::verify_ancestry_proof(
110+
beacon_block_root,
111+
execution_proof.header.slot,
112+
&proof.header_branch,
113+
proof.finalized_block_root,
114+
)?;
115+
},
116+
None => {
117+
// If the ancestry proof is not provided, we expect this beacon header to be a
118+
// finalized beacon header. We need to check that the header hash matches the
119+
// finalized header root at the expected slot.
120+
let state = <FinalizedBeaconState<T>>::get(beacon_block_root)
121+
.ok_or(Error::<T>::ExpectedFinalizedHeaderNotStored)?;
122+
if execution_proof.header.slot != state.slot {
123+
return Err(Error::<T>::ExpectedFinalizedHeaderNotStored.into())
124+
}
125+
},
126+
}
127+
128+
Ok(())
129+
}
130+
131+
/// Verify that `block_root` is an ancestor of `finalized_block_root` Used to prove that
132+
/// an execution header is an ancestor of a finalized header (i.e. the blocks are
133+
/// on the same chain).
134+
fn verify_ancestry_proof(
135+
block_root: H256,
136+
block_slot: u64,
137+
block_root_proof: &[H256],
138+
finalized_block_root: H256,
139+
) -> DispatchResult {
140+
let state = <FinalizedBeaconState<T>>::get(finalized_block_root)
141+
.ok_or(Error::<T>::ExpectedFinalizedHeaderNotStored)?;
142+
143+
ensure!(block_slot < state.slot, Error::<T>::HeaderNotFinalized);
144+
145+
let index_in_array = block_slot % (SLOTS_PER_HISTORICAL_ROOT as u64);
146+
let leaf_index = (SLOTS_PER_HISTORICAL_ROOT as u64) + index_in_array;
147+
148+
ensure!(
149+
verify_merkle_branch(
150+
block_root,
151+
block_root_proof,
152+
leaf_index as usize,
153+
config::BLOCK_ROOT_AT_INDEX_DEPTH,
154+
state.block_roots_root
155+
),
156+
Error::<T>::InvalidAncestryMerkleProof
157+
);
158+
159+
Ok(())
160+
}
93161
}

0 commit comments

Comments
 (0)