Skip to content

Commit

Permalink
Snowbridge: Synchronize from Snowfork repository (#3761) (#4829)
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
4 people authored Jun 25, 2024
1 parent e046d3b commit 6dba07c
Show file tree
Hide file tree
Showing 34 changed files with 1,050 additions and 1,292 deletions.
272 changes: 179 additions & 93 deletions bridges/snowbridge/parachain/pallets/ethereum-client/fixtures/src/lib.rs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,6 @@ mod benchmarks {
Ok(())
}

#[benchmark]
fn submit_execution_header() -> Result<(), BenchmarkError> {
let caller: T::AccountId = whitelisted_caller();
let checkpoint_update = make_checkpoint();
let finalized_header_update = make_finalized_header_update();
let execution_header_update = make_execution_header_update();
let execution_header_hash = execution_header_update.execution_header.block_hash();
EthereumBeaconClient::<T>::process_checkpoint_update(&checkpoint_update)?;
EthereumBeaconClient::<T>::process_update(&finalized_header_update)?;

#[extrinsic_call]
_(RawOrigin::Signed(caller.clone()), Box::new(*execution_header_update));

assert!(<ExecutionHeaders<T>>::contains_key(execution_header_hash));

Ok(())
}

#[benchmark(extra)]
fn bls_fast_aggregate_verify_pre_aggregated() -> Result<(), BenchmarkError> {
EthereumBeaconClient::<T>::process_checkpoint_update(&make_checkpoint())?;
Expand Down
138 changes: 103 additions & 35 deletions bridges/snowbridge/parachain/pallets/ethereum-client/src/impls.rs
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, *},
Expand All @@ -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)?;

Expand All @@ -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(())
}
}
Expand All @@ -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),
Expand All @@ -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(())
}
}
Loading

0 comments on commit 6dba07c

Please sign in to comment.