Skip to content

Commit 0a98610

Browse files
claravanstadenyrongvgeddessvyatonik
authored andcommitted
Snowbridge: Synchronize from Snowfork repository (#3761)
This PR includes the following 2 improvements: ## Ethereum Client Author: @yrong ### Original Upstream PRs - Snowfork#123 - Snowfork#125 ### Description The Ethereum client syncs beacon headers as they are finalized, and imports every execution header. When a message is received, it is verified against the import execution header. This is unnecessary, since the execution header can be sent with the message as proof. The recent Deneb Ethereum upgrade made it easier to locate the relevant beacon header from an execution header, and so this improvement was made possible. This resolves a concern @svyatonik had in our initial Rococo PR: #2522 (comment) ## Inbound Queue Author: @yrong ### Original Upstream PR - Snowfork#118 ### Description When the AH sovereign account (who pays relayer rewards) is depleted, the inbound message will not fail. The relayer just will not receive rewards. Both these changes were done by @yrong, many thanks. ❤️ --------- Co-authored-by: claravanstaden <Cats 4 life!> Co-authored-by: Ron <[email protected]> Co-authored-by: Vincent Geddes <[email protected]> Co-authored-by: Svyatoslav Nikolsky <[email protected]>
1 parent b659b7d commit 0a98610

File tree

35 files changed

+1123
-1307
lines changed

35 files changed

+1123
-1307
lines changed

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

Lines changed: 179 additions & 93 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 <[email protected]>
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)