-
Notifications
You must be signed in to change notification settings - Fork 707
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
This is a cherry-pick from master of #3761, excluding PR Snowfork#118 Expected patches for (1.7.0): - name: snowbridge-pallet-ethereum-client - name: snowbridge-pallet-inbound-queue - name: snowbridge-beacon-primitives - name: snowbridge-core - name: snowbridge-runtime-test-common --------- Co-authored-by: Ron <[email protected]> Co-authored-by: Vincent Geddes <[email protected]> Co-authored-by: Svyatoslav Nikolsky <[email protected]>
- Loading branch information
1 parent
e046d3b
commit 6dba07c
Showing
34 changed files
with
1,050 additions
and
1,292 deletions.
There are no files selected for viewing
272 changes: 179 additions & 93 deletions
272
bridges/snowbridge/parachain/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.