Skip to content
This repository has been archived by the owner on Oct 22, 2024. It is now read-only.

Commit

Permalink
Inline execution update into proof of inbound message & Remove execut…
Browse files Browse the repository at this point in the history
…ion 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>
  • Loading branch information
yrong and vgeddes authored Mar 20, 2024
1 parent 8be52c9 commit 4c850e8
Show file tree
Hide file tree
Showing 36 changed files with 1,224 additions and 1,196 deletions.
274 changes: 180 additions & 94 deletions bridges/snowbridge/pallets/ethereum-client/fixtures/src/lib.rs

Large diffs are not rendered by default.

18 changes: 0 additions & 18 deletions bridges/snowbridge/pallets/ethereum-client/src/benchmarking/mod.rs
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/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 <hello@snowfork.com>
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 4c850e8

Please sign in to comment.