Skip to content

Commit

Permalink
Snowbridge free consensus updates (#5201)
Browse files Browse the repository at this point in the history
Allow free Snowbridge consensus updates, if the header interval is
larger than the configured value (set to 32, so once a epoch).

This PR also moves the Rococo Snowbridge pallet config into its own
module.

Original PR: Snowfork#159

---------

Co-authored-by: Francisco Aguirre <franciscoaguirreperez@gmail.com>
  • Loading branch information
2 people authored and x3c41a committed Sep 4, 2024
1 parent 63a1e49 commit f3b9f26
Show file tree
Hide file tree
Showing 10 changed files with 498 additions and 282 deletions.
53 changes: 42 additions & 11 deletions bridges/snowbridge/pallets/ethereum-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ mod tests;
mod benchmarking;

use frame_support::{
dispatch::DispatchResult, pallet_prelude::OptionQuery, traits::Get, transactional,
dispatch::{DispatchResult, PostDispatchInfo},
pallet_prelude::OptionQuery,
traits::Get,
transactional,
};
use frame_system::ensure_signed;
use snowbridge_beacon_primitives::{
Expand Down Expand Up @@ -82,6 +85,9 @@ pub mod pallet {
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
#[pallet::constant]
type ForkVersions: Get<ForkVersions>;
/// Minimum gap between finalized headers for an update to be free.
#[pallet::constant]
type FreeHeadersInterval: Get<u32>;
type WeightInfo: WeightInfo;
}

Expand Down Expand Up @@ -204,11 +210,10 @@ pub mod pallet {
#[transactional]
/// Submits a new finalized beacon header update. The update may contain the next
/// sync committee.
pub fn submit(origin: OriginFor<T>, update: Box<Update>) -> DispatchResult {
pub fn submit(origin: OriginFor<T>, update: Box<Update>) -> DispatchResultWithPostInfo {
ensure_signed(origin)?;
ensure!(!Self::operating_mode().is_halted(), Error::<T>::Halted);
Self::process_update(&update)?;
Ok(())
Self::process_update(&update)
}

/// Halt or resume all pallet operations. May only be called by root.
Expand Down Expand Up @@ -280,10 +285,9 @@ pub mod pallet {
Ok(())
}

pub(crate) fn process_update(update: &Update) -> DispatchResult {
pub(crate) fn process_update(update: &Update) -> DispatchResultWithPostInfo {
Self::verify_update(update)?;
Self::apply_update(update)?;
Ok(())
Self::apply_update(update)
}

/// References and strictly follows <https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#validate_light_client_update>
Expand Down Expand Up @@ -432,8 +436,9 @@ pub mod pallet {
/// Reference and strictly follows <https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#apply_light_client_update
/// Applies a finalized beacon header update to the beacon client. If a next sync committee
/// is present in the update, verify the sync committee by converting it to a
/// SyncCommitteePrepared type. Stores the provided finalized header.
fn apply_update(update: &Update) -> DispatchResult {
/// SyncCommitteePrepared type. Stores the provided finalized header. Updates are free
/// if the certain conditions specified in `check_refundable` are met.
fn apply_update(update: &Update) -> DispatchResultWithPostInfo {
let latest_finalized_state =
FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get())
.ok_or(Error::<T>::NotBootstrapped)?;
Expand Down Expand Up @@ -465,11 +470,17 @@ pub mod pallet {
});
};

let pays_fee = Self::check_refundable(update, latest_finalized_state.slot);
let actual_weight = match update.next_sync_committee_update {
None => T::WeightInfo::submit(),
Some(_) => T::WeightInfo::submit_with_sync_committee(),
};

if update.finalized_header.slot > latest_finalized_state.slot {
Self::store_finalized_header(update.finalized_header, update.block_roots_root)?;
}

Ok(())
Ok(PostDispatchInfo { actual_weight: Some(actual_weight), pays_fee })
}

/// Computes the signing root for a given beacon header and domain. The hash tree root
Expand Down Expand Up @@ -634,11 +645,31 @@ pub mod pallet {
config::SLOTS_PER_EPOCH as u64,
));
let domain_type = config::DOMAIN_SYNC_COMMITTEE.to_vec();
// Domains are used for for seeds, for signatures, and for selecting aggregators.
// Domains are used for seeds, for signatures, and for selecting aggregators.
let domain = Self::compute_domain(domain_type, fork_version, validators_root)?;
// Hash tree root of SigningData - object root + domain
let signing_root = Self::compute_signing_root(header, domain)?;
Ok(signing_root)
}

/// Updates are free if the update is successful and the interval between the latest
/// finalized header in storage and the newly imported header is large enough. All
/// successful sync committee updates are free.
pub(super) fn check_refundable(update: &Update, latest_slot: u64) -> Pays {
// If the sync committee was successfully updated, the update may be free.
if update.next_sync_committee_update.is_some() {
return Pays::No;
}

// If the latest finalized header is larger than the minimum slot interval, the header
// import transaction is free.
if update.finalized_header.slot >=
latest_slot.saturating_add(T::FreeHeadersInterval::get() as u64)
{
return Pays::No;
}

Pays::Yes
}
}
}
4 changes: 4 additions & 0 deletions bridges/snowbridge/pallets/ethereum-client/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use sp_std::default::Default;
use std::{fs::File, path::PathBuf};

type Block = frame_system::mocking::MockBlock<Test>;
use frame_support::traits::ConstU32;
use sp_runtime::BuildStorage;

fn load_fixture<T>(basename: String) -> Result<T, serde_json::Error>
Expand Down Expand Up @@ -108,9 +109,12 @@ parameter_types! {
};
}

pub const FREE_SLOTS_INTERVAL: u32 = config::SLOTS_PER_EPOCH as u32;

impl ethereum_beacon_client::Config for Test {
type RuntimeEvent = RuntimeEvent;
type ForkVersions = ChainForkVersions;
type FreeHeadersInterval = ConstU32<FREE_SLOTS_INTERVAL>;
type WeightInfo = ();
}

Expand Down
Loading

0 comments on commit f3b9f26

Please sign in to comment.