forked from paritytech/polkadot-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Snowbridge: Synchronize from Snowfork repository (paritytech#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: paritytech#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]>
- Loading branch information
Showing
35 changed files
with
1,123 additions
and
1,307 deletions.
There are no files selected for viewing
272 changes: 179 additions & 93 deletions
272
bridges/snowbridge/pallets/ethereum-client/fixtures/src/lib.rs
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// SPDX-FileCopyrightText: 2023 Snowfork <[email protected]> | ||
use super::*; | ||
use frame_support::ensure; | ||
use primitives::ExecutionProof; | ||
|
||
use snowbridge_core::inbound::{ | ||
VerificationError::{self, *}, | ||
|
@@ -14,32 +16,13 @@ impl<T: Config> Verifier for Pallet<T> { | |
/// the log should be in the beacon client storage, meaning it has been verified and is an | ||
/// ancestor of a finalized beacon block. | ||
fn verify(event_log: &Log, proof: &Proof) -> Result<(), VerificationError> { | ||
log::info!( | ||
target: "ethereum-client", | ||
"💫 Verifying message with block hash {}", | ||
proof.block_hash, | ||
); | ||
Self::verify_execution_proof(&proof.execution_proof) | ||
.map_err(|e| InvalidExecutionProof(e.into()))?; | ||
|
||
let header = <ExecutionHeaderBuffer<T>>::get(proof.block_hash).ok_or(HeaderNotFound)?; | ||
|
||
let receipt = match Self::verify_receipt_inclusion(header.receipts_root, proof) { | ||
Ok(receipt) => receipt, | ||
Err(err) => { | ||
log::error!( | ||
target: "ethereum-client", | ||
"💫 Verification of receipt inclusion failed for block {}: {:?}", | ||
proof.block_hash, | ||
err | ||
); | ||
return Err(err) | ||
}, | ||
}; | ||
|
||
log::trace!( | ||
target: "ethereum-client", | ||
"💫 Verified receipt inclusion for transaction at index {} in block {}", | ||
proof.tx_index, proof.block_hash, | ||
); | ||
let receipt = Self::verify_receipt_inclusion( | ||
proof.execution_proof.execution_header.receipts_root(), | ||
&proof.receipt_proof.1, | ||
)?; | ||
|
||
event_log.validate().map_err(|_| InvalidLog)?; | ||
|
||
|
@@ -53,18 +36,11 @@ impl<T: Config> Verifier for Pallet<T> { | |
if !receipt.contains_log(&event_log) { | ||
log::error!( | ||
target: "ethereum-client", | ||
"💫 Event log not found in receipt for transaction at index {} in block {}", | ||
proof.tx_index, proof.block_hash, | ||
"💫 Event log not found in receipt for transaction", | ||
); | ||
return Err(LogNotFound) | ||
} | ||
|
||
log::info!( | ||
target: "ethereum-client", | ||
"💫 Receipt verification successful for {}", | ||
proof.block_hash, | ||
); | ||
|
||
Ok(()) | ||
} | ||
} | ||
|
@@ -74,9 +50,9 @@ impl<T: Config> Pallet<T> { | |
/// `proof.block_hash`. | ||
pub fn verify_receipt_inclusion( | ||
receipts_root: H256, | ||
proof: &Proof, | ||
receipt_proof: &[Vec<u8>], | ||
) -> Result<Receipt, VerificationError> { | ||
let result = verify_receipt_proof(receipts_root, &proof.data.1).ok_or(InvalidProof)?; | ||
let result = verify_receipt_proof(receipts_root, receipt_proof).ok_or(InvalidProof)?; | ||
|
||
match result { | ||
Ok(receipt) => Ok(receipt), | ||
|
@@ -90,4 +66,96 @@ impl<T: Config> Pallet<T> { | |
}, | ||
} | ||
} | ||
|
||
/// Validates an execution header with ancestry_proof against a finalized checkpoint on | ||
/// chain.The beacon header containing the execution header is sent, plus the execution header, | ||
/// along with a proof that the execution header is rooted in the beacon header body. | ||
pub(crate) fn verify_execution_proof(execution_proof: &ExecutionProof) -> DispatchResult { | ||
let latest_finalized_state = | ||
FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get()) | ||
.ok_or(Error::<T>::NotBootstrapped)?; | ||
// Checks that the header is an ancestor of a finalized header, using slot number. | ||
ensure!( | ||
execution_proof.header.slot <= latest_finalized_state.slot, | ||
Error::<T>::HeaderNotFinalized | ||
); | ||
|
||
// Gets the hash tree root of the execution header, in preparation for the execution | ||
// header proof (used to check that the execution header is rooted in the beacon | ||
// header body. | ||
let execution_header_root: H256 = execution_proof | ||
.execution_header | ||
.hash_tree_root() | ||
.map_err(|_| Error::<T>::BlockBodyHashTreeRootFailed)?; | ||
|
||
ensure!( | ||
verify_merkle_branch( | ||
execution_header_root, | ||
&execution_proof.execution_branch, | ||
config::EXECUTION_HEADER_SUBTREE_INDEX, | ||
config::EXECUTION_HEADER_DEPTH, | ||
execution_proof.header.body_root | ||
), | ||
Error::<T>::InvalidExecutionHeaderProof | ||
); | ||
|
||
let beacon_block_root: H256 = execution_proof | ||
.header | ||
.hash_tree_root() | ||
.map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?; | ||
|
||
match &execution_proof.ancestry_proof { | ||
Some(proof) => { | ||
Self::verify_ancestry_proof( | ||
beacon_block_root, | ||
execution_proof.header.slot, | ||
&proof.header_branch, | ||
proof.finalized_block_root, | ||
)?; | ||
}, | ||
None => { | ||
// If the ancestry proof is not provided, we expect this beacon header to be a | ||
// finalized beacon header. We need to check that the header hash matches the | ||
// finalized header root at the expected slot. | ||
let state = <FinalizedBeaconState<T>>::get(beacon_block_root) | ||
.ok_or(Error::<T>::ExpectedFinalizedHeaderNotStored)?; | ||
if execution_proof.header.slot != state.slot { | ||
return Err(Error::<T>::ExpectedFinalizedHeaderNotStored.into()) | ||
} | ||
}, | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
/// Verify that `block_root` is an ancestor of `finalized_block_root` Used to prove that | ||
/// an execution header is an ancestor of a finalized header (i.e. the blocks are | ||
/// on the same chain). | ||
fn verify_ancestry_proof( | ||
block_root: H256, | ||
block_slot: u64, | ||
block_root_proof: &[H256], | ||
finalized_block_root: H256, | ||
) -> DispatchResult { | ||
let state = <FinalizedBeaconState<T>>::get(finalized_block_root) | ||
.ok_or(Error::<T>::ExpectedFinalizedHeaderNotStored)?; | ||
|
||
ensure!(block_slot < state.slot, Error::<T>::HeaderNotFinalized); | ||
|
||
let index_in_array = block_slot % (SLOTS_PER_HISTORICAL_ROOT as u64); | ||
let leaf_index = (SLOTS_PER_HISTORICAL_ROOT as u64) + index_in_array; | ||
|
||
ensure!( | ||
verify_merkle_branch( | ||
block_root, | ||
block_root_proof, | ||
leaf_index as usize, | ||
config::BLOCK_ROOT_AT_INDEX_DEPTH, | ||
state.block_roots_root | ||
), | ||
Error::<T>::InvalidAncestryMerkleProof | ||
); | ||
|
||
Ok(()) | ||
} | ||
} |
Oops, something went wrong.