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

Commit

Permalink
Execution Header processing and syncing (paritytech#638)
Browse files Browse the repository at this point in the history
* Start working on beacon block sync

* Progress on beacon block verification

* Beacon block verification

* Block proof progress

* Updates ssz_rs version with transactions panic fix.

* Use tested individual merkleization functions in beacon block proof

* Beacon block proof works

* Beacon block with transactions and attester slashing also works

* Pins byte-slice-cast version, checks rust version

* remove ssz_rs rev

* Tries updating Rust version

* Updates ssz-rs name and version

* Updates ssz-rs version

* Updates ssz-rs-derive too

* Moves Beacon structs into primitives

* Adds new beacon primitive files

* Fixes no-std issues.

* Cleanup and refactor.

* More cleanup

* Store execution header

* Getting beacon block and mapping to scale.

* More mappings.

* Send header update to relayer.

* Fixed extrinsic call pallet name.

* Adds condition to check if finalized header has been synced already

* Logic fix.

* Adds new method and fixes return paths.

* Fixing bugs while testing.

* Attempting to debug header verification

* Testing header verification.

* Adds sync aggregate to block update.

* Adds sync aggregate to block update.

* Testing changes

* Hashes the transactions before sending it to the light client.

* Working on sending block to lc.

* Finish up the execution header processing.

* Tries to resolve ambiguous import error.

* Attempts to fix go modules issue

* Cleaned and refactor.

Co-authored-by: claravanstaden <Cats 4 life!>
  • Loading branch information
claravanstaden authored Jun 6, 2022
1 parent e342ca3 commit abf5d2f
Show file tree
Hide file tree
Showing 20 changed files with 4,205 additions and 2,391 deletions.
4 changes: 0 additions & 4 deletions parachain/pallets/ethereum-beacon-client/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@ pub const MAX_VALIDATORS_PER_COMMITTEE: usize = 2048;

pub const DEPOSIT_CONTRACT_TREE_DEPTH: usize = 32;

pub const MAX_BYTES_PER_TRANSACTION: usize = 1073741824;

pub const MAX_TRANSACTIONS_PER_PAYLOAD: usize = 1048576;

pub const MAX_EXTRA_DATA_BYTES: usize = 32;

pub const SYNC_COMMITTEE_SIZE: usize = 512;
208 changes: 157 additions & 51 deletions parachain/pallets/ethereum-beacon-client/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ use sp_core::H256;
use sp_io::hashing::sha2_256;
use sp_runtime::RuntimeDebug;
use sp_std::prelude::*;
use snowbridge_beacon::Header as ExecutionHeader;
use snowbridge_beacon::{SyncCommittee, BeaconHeader, SyncAggregate, ForkData, Root, Domain, PublicKey, SigningData};
use snowbridge_beacon::{SyncCommittee, BeaconHeader, SyncAggregate, ForkData, Root, Domain, PublicKey, SigningData, ExecutionHeader, BeaconBlock};

const SLOTS_PER_EPOCH: u64 = 32;

Expand Down Expand Up @@ -73,14 +72,12 @@ pub struct FinalizedHeaderUpdate {
}

#[derive(Clone, Default, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct HeaderUpdate {
pub attested_header: BeaconHeader,
pub execution_header: ExecutionHeader,
pub struct BlockUpdate {
pub block: BeaconBlock,
pub sync_aggregate: SyncAggregate,
pub fork_version: ForkVersion,
}


#[derive(Clone, Default, Encode, Decode, PartialEq, RuntimeDebug, TypeInfo)]
pub struct Genesis {
pub validators_root: Root,
Expand All @@ -97,6 +94,8 @@ pub mod pallet {
use frame_system::pallet_prelude::*;

use milagro_bls::{AggregatePublicKey, AggregateSignature, AmclError, Signature};
use sp_core::H160;

#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::without_storage_info]
Expand Down Expand Up @@ -126,6 +125,7 @@ pub mod pallet {
InvalidHash,
SignatureVerificationFailed,
NoBranchExpected,
HeaderNotFinalized,
}

#[pallet::hooks]
Expand All @@ -150,7 +150,10 @@ pub mod pallet {
StorageMap<_, Identity, u64, SyncCommittee, ValueQuery>;

#[pallet::storage]
pub(super) type ChainGenesis<T: Config> = StorageValue<_, Genesis, ValueQuery>;
pub(super) type ValidatorsRoot<T: Config> = StorageValue<_, H256, ValueQuery>;

#[pallet::storage]
pub(super) type LatestFinalizedHeaderSlot<T: Config> = StorageValue<_, u64, ValueQuery>;

#[pallet::genesis_config]
pub struct GenesisConfig {}
Expand Down Expand Up @@ -244,7 +247,7 @@ pub mod pallet {

log::trace!(
target: "ethereum-beacon-client",
"💫 Received finalized header update for slot {}, processing and importing finalized header.",
"💫 Received finalized header for slot {}.",
slot
);

Expand All @@ -259,7 +262,43 @@ pub mod pallet {

log::trace!(
target: "ethereum-beacon-client",
"💫 Finalized header processing and importing at slot {} succeeded.",
"💫 Stored finalized beacon header at slot {}.",
slot
);

Ok(())
}

#[pallet::weight(1_000_000)]
#[transactional]
pub fn import_execution_header(
origin: OriginFor<T>,
update: BlockUpdate,
) -> DispatchResult {
let _sender = ensure_signed(origin)?;

let slot = update.block.slot;
let block_hash = update.block.body.execution_payload.block_hash;

log::trace!(
target: "ethereum-beacon-client",
"💫 Received header update for slot {}.",
slot
);

if let Err(err) = Self::process_header(update) {
log::error!(
target: "ethereum-beacon-client",
"Header update failed with error {:?}",
err
);
return Err(err);
}

log::trace!(
target: "ethereum-beacon-client",
"💫 Stored execution header {} at beacon slot {}.",
block_hash,
slot
);

Expand All @@ -274,7 +313,7 @@ pub mod pallet {
let _sender = ensure_signed(origin)?;

log::trace!(
target: "ethereum-beacon-light-client",
target: "ethereum-beacon-client",
"💫 Received transaction to be validated.",
);

Expand All @@ -299,15 +338,16 @@ pub mod pallet {
.map_err(|_| DispatchError::Other("Header hash tree root failed"))?.into();
Self::store_finalized_header(block_root, initial_sync.header);

Self::store_genesis(Genesis { validators_root: initial_sync.validators_root });
Self::store_validators_root( initial_sync.validators_root );

Ok(())
}

fn process_sync_committee_period_update(
update: SyncCommitteePeriodUpdate,
) -> DispatchResult {
let sync_committee_bits = Self::convert_to_binary(update.sync_aggregate.sync_committee_bits.clone());
let sync_committee_bits = merkleization::get_sync_committee_bits(update.sync_aggregate.sync_committee_bits.clone())
.map_err(|_| DispatchError::Other("Couldn't process sync committee bits"))?;
Self::sync_committee_participation_is_supermajority(sync_committee_bits.clone())?;
Self::verify_sync_committee(
update.next_sync_committee.clone(),
Expand All @@ -331,15 +371,15 @@ pub mod pallet {
Self::store_sync_committee(current_period + 1, update.next_sync_committee);

let current_sync_committee = <SyncCommittees<T>>::get(current_period);
let genesis = <ChainGenesis<T>>::get();
let validators_root = <ValidatorsRoot<T>>::get();

Self::verify_signed_header(
sync_committee_bits,
update.sync_aggregate.sync_committee_signature,
current_sync_committee.pubkeys,
update.fork_version,
update.attested_header,
genesis.validators_root,
validators_root,
)?;

Self::store_finalized_header(block_root, update.finalized_header);
Expand All @@ -348,7 +388,8 @@ pub mod pallet {
}

fn process_finalized_header(update: FinalizedHeaderUpdate) -> DispatchResult {
let sync_committee_bits = Self::convert_to_binary(update.sync_aggregate.sync_committee_bits.clone());
let sync_committee_bits = merkleization::get_sync_committee_bits(update.sync_aggregate.sync_committee_bits.clone())
.map_err(|_| DispatchError::Other("Couldn't process sync committee bits"))?;
Self::sync_committee_participation_is_supermajority(sync_committee_bits.clone())?;

let block_root: H256 = merkleization::hash_tree_root_beacon_header(update.finalized_header.clone())
Expand All @@ -362,25 +403,81 @@ pub mod pallet {
)?;

let current_period = Self::compute_current_sync_period(update.attested_header.slot);
let sync_committee = <SyncCommittees<T>>::get(current_period);
if (SyncCommittee { pubkeys: vec![], aggregate_pubkey: PublicKey([0; 48]) }) == sync_committee {
return Err(Error::<T>::SyncCommitteeMissing.into());
}
let genesis = <ChainGenesis<T>>::get();
let sync_committee = Self::get_sync_committee_for_period(current_period)?;

let validators_root = <ValidatorsRoot<T>>::get();
Self::verify_signed_header(
sync_committee_bits,
update.sync_aggregate.sync_committee_signature,
sync_committee.pubkeys,
update.fork_version,
update.attested_header,
genesis.validators_root,
validators_root,
)?;

Self::store_finalized_header(block_root, update.finalized_header);

Ok(())
}

fn process_header(update: BlockUpdate) -> DispatchResult {
let latest_finalized_header_slot = <LatestFinalizedHeaderSlot<T>>::get();
let block_slot = update.block.slot;
if block_slot > latest_finalized_header_slot {
return Err(Error::<T>::HeaderNotFinalized.into());
}

let current_period = Self::compute_current_sync_period(update.block.slot);
let sync_committee = Self::get_sync_committee_for_period(current_period)?;

let body_root = merkleization::hash_tree_root_beacon_body(update.block.body.clone())
.map_err(|_| DispatchError::Other("Beacon body hash tree root failed"))?;

let header = BeaconHeader{
slot: update.block.slot,
proposer_index: update.block.proposer_index,
parent_root: update.block.parent_root,
state_root: update.block.state_root,
body_root: body_root.into(),
};

let validators_root = <ValidatorsRoot<T>>::get();
let sync_committee_bits = merkleization::get_sync_committee_bits(update.sync_aggregate.sync_committee_bits.clone())
.map_err(|_| DispatchError::Other("Couldn't process sync committee bits"))?;
Self::verify_signed_header(
sync_committee_bits,
update.sync_aggregate.sync_committee_signature,
sync_committee.pubkeys,
update.fork_version,
header,
validators_root,
)?;

let execution_payload = update.block.body.execution_payload;

let mut fee_recipient = [0u8; 20];
fee_recipient[0..20].copy_from_slice(&(execution_payload.fee_recipient.as_slice()));

Self::store_execution_header(execution_payload.block_hash, ExecutionHeader{
parent_hash: execution_payload.parent_hash,
fee_recipient: H160::from(fee_recipient),
state_root: execution_payload.state_root,
receipts_root: execution_payload.receipts_root,
logs_bloom: execution_payload.logs_bloom,
prev_randao: execution_payload.prev_randao,
block_number: execution_payload.block_number,
gas_used: execution_payload.gas_used,
gas_limit: execution_payload.gas_limit,
timestamp: execution_payload.timestamp,
extra_data: execution_payload.extra_data,
base_fee_per_gas: execution_payload.base_fee_per_gas,
block_hash: execution_payload.block_hash,
transactions_root: execution_payload.transactions_root,
});

Ok(())
}

pub(super) fn verify_signed_header(
sync_committee_bits: Vec<u8>,
sync_committee_signature: Vec<u8>,
Expand Down Expand Up @@ -458,8 +555,10 @@ pub mod pallet {
let beacon_header_root = merkleization::hash_tree_root_beacon_header(beacon_header)
.map_err(|_| DispatchError::Other("Beacon header hash tree root failed"))?;

let header_hash_tree_root: H256 = beacon_header_root.into();

let hash_root = merkleization::hash_tree_root_signing_data(SigningData {
object_root: beacon_header_root.into(),
object_root: header_hash_tree_root,
domain,
})
.map_err(|_| DispatchError::Other("Signing root hash tree root failed"))?;
Expand Down Expand Up @@ -518,11 +617,35 @@ pub mod pallet {
}

fn store_finalized_header(block_root: H256, header: BeaconHeader) {
<FinalizedBeaconHeaders<T>>::insert(block_root, header.clone());
let slot = header.slot;

<FinalizedBeaconHeaders<T>>::insert(block_root, header);

log::trace!(
target: "ethereum-beacon-client",
"💫 Saved finalized block root {} at slot {}.",
block_root,
slot
);

let latest_finalized_header_slot = <LatestFinalizedHeaderSlot<T>>::get();

if slot > latest_finalized_header_slot {
log::trace!(
target: "ethereum-beacon-client",
"💫 Updated latest finalized slot to {}.",
slot
);
<LatestFinalizedHeaderSlot<T>>::set(slot);
}
}

fn store_genesis(genesis: Genesis) {
<ChainGenesis<T>>::put(genesis);
fn store_execution_header(block_root: H256, header: ExecutionHeader) {
<ExecutionHeaders<T>>::insert(block_root, header);
}

fn store_validators_root(validators_root: H256) {
<ValidatorsRoot<T>>::set(validators_root);
}

/// Sums the bit vector of sync committee particpation.
Expand Down Expand Up @@ -616,32 +739,6 @@ pub mod pallet {
return value == root;
}

pub(super) fn convert_to_binary(input: Vec<u8>) -> Vec<u8> {
let mut result = Vec::new();

for input_decimal in input.iter() {
let mut tmp = Vec::new();
let mut remaining = *input_decimal;

while remaining > 0 {
let remainder = remaining % 2;
tmp.push(remainder);
remaining = remaining / 2;
}

// pad binary with 0s if length is less than 7
if tmp.len() < 8 {
for _i in tmp.len()..8 {
tmp.push(0)
}
}

result.append(&mut tmp);
}

result
}

pub(super) fn sync_committee_participation_is_supermajority(sync_committee_bits: Vec<u8>) -> DispatchResult {
let sync_committee_sum = Self::get_sync_committee_sum(sync_committee_bits.clone());
ensure!(
Expand All @@ -651,5 +748,14 @@ pub mod pallet {

Ok(())
}

pub(super) fn get_sync_committee_for_period(period: u64) -> Result<SyncCommittee, DispatchError> {
let sync_committee = <SyncCommittees<T>>::get(period);
if (SyncCommittee { pubkeys: vec![], aggregate_pubkey: PublicKey([0; 48]) }) == sync_committee {
return Err(Error::<T>::SyncCommitteeMissing.into());
}

Ok(sync_committee)
}
}
}
Loading

0 comments on commit abf5d2f

Please sign in to comment.