mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-31 05:11:02 +00:00
Adds Snowbridge to Rococo runtime (#2522)
# Description Adds Snowbridge to the Rococo bridge hub runtime. Includes config changes required in Rococo asset hub. --------- Co-authored-by: Alistair Singh <alistair.singh7@gmail.com> Co-authored-by: ron <yrong1997@gmail.com> Co-authored-by: Vincent Geddes <vincent.geddes@hey.com> Co-authored-by: claravanstaden <Cats 4 life!>
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
[package]
|
||||
name = "snowbridge-ethereum-beacon-client"
|
||||
description = "Snowbridge Beacon Client Pallet"
|
||||
version = "0.0.1"
|
||||
edition = "2021"
|
||||
authors = ["Snowfork <contact@snowfork.com>"]
|
||||
repository = "https://github.com/Snowfork/snowbridge"
|
||||
license = "Apache-2.0"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = ["x86_64-unknown-linux-gnu"]
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.188", optional = true }
|
||||
serde_json = { version = "1.0.96", optional = true }
|
||||
codec = { version = "3.6.1", package = "parity-scale-codec", default-features = false, features = ["derive"] }
|
||||
scale-info = { version = "2.9.0", default-features = false, features = ["derive"] }
|
||||
ssz_rs = { version = "0.9.0", default-features = false }
|
||||
ssz_rs_derive = { version = "0.9.0", default-features = false }
|
||||
byte-slice-cast = { version = "1.2.1", default-features = false }
|
||||
rlp = { version = "0.5.2", default-features = false }
|
||||
hex-literal = { version = "0.4.1", optional = true }
|
||||
log = { version = "0.4.20", default-features = false }
|
||||
|
||||
frame-benchmarking = { path = "../../../../../substrate/frame/benchmarking", default-features = false, optional = true }
|
||||
frame-support = { path = "../../../../../substrate/frame/support", default-features = false }
|
||||
frame-system = { path = "../../../../../substrate/frame/system", default-features = false }
|
||||
sp-core = { path = "../../../../../substrate/primitives/core", default-features = false }
|
||||
sp-std = { path = "../../../../../substrate/primitives/std", default-features = false }
|
||||
sp-runtime = { path = "../../../../../substrate/primitives/runtime", default-features = false }
|
||||
sp-io = { path = "../../../../../substrate/primitives/io", default-features = false, optional = true }
|
||||
|
||||
snowbridge-core = { path = "../../primitives/core", default-features = false }
|
||||
snowbridge-ethereum = { path = "../../primitives/ethereum", default-features = false }
|
||||
primitives = { package = "snowbridge-beacon-primitives", path = "../../primitives/beacon", default-features = false }
|
||||
static_assertions = { version = "1.1.0", default-features = false }
|
||||
bp-runtime = { path = "../../../../../bridges/primitives/runtime", default-features = false }
|
||||
pallet-timestamp = { path = "../../../../../substrate/frame/timestamp", default-features = false, optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.5"
|
||||
sp-keyring = { path = "../../../../../substrate/primitives/keyring" }
|
||||
serde_json = "1.0.96"
|
||||
hex-literal = "0.4.1"
|
||||
pallet-timestamp = { path = "../../../../../substrate/frame/timestamp" }
|
||||
sp-io = { path = "../../../../../substrate/primitives/io" }
|
||||
serde = "1.0.188"
|
||||
|
||||
[features]
|
||||
default = ["std"]
|
||||
fuzzing = [
|
||||
"hex-literal",
|
||||
"pallet-timestamp",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sp-io",
|
||||
]
|
||||
std = [
|
||||
"bp-runtime/std",
|
||||
"byte-slice-cast/std",
|
||||
"codec/std",
|
||||
"frame-support/std",
|
||||
"frame-system/std",
|
||||
"log/std",
|
||||
"pallet-timestamp/std",
|
||||
"primitives/std",
|
||||
"rlp/std",
|
||||
"scale-info/std",
|
||||
"serde",
|
||||
"snowbridge-core/std",
|
||||
"snowbridge-ethereum/std",
|
||||
"sp-core/std",
|
||||
"sp-io/std",
|
||||
"sp-runtime/std",
|
||||
"sp-std/std",
|
||||
"ssz_rs/std",
|
||||
'frame-benchmarking/std',
|
||||
]
|
||||
runtime-benchmarks = [
|
||||
"beacon-spec-mainnet",
|
||||
"frame-benchmarking/runtime-benchmarks",
|
||||
"frame-support/runtime-benchmarks",
|
||||
"frame-system/runtime-benchmarks",
|
||||
"hex-literal",
|
||||
"pallet-timestamp?/runtime-benchmarks",
|
||||
"snowbridge-core/runtime-benchmarks",
|
||||
"sp-runtime/runtime-benchmarks",
|
||||
]
|
||||
try-runtime = [
|
||||
"frame-support/try-runtime",
|
||||
"frame-system/try-runtime",
|
||||
"pallet-timestamp?/try-runtime",
|
||||
"sp-runtime/try-runtime",
|
||||
]
|
||||
beacon-spec-mainnet = []
|
||||
@@ -0,0 +1,88 @@
|
||||
# Motivation
|
||||
Demonstrate that
|
||||
[FastAggregateVerify](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-04#section-3.3.4) is the most
|
||||
expensive call in ethereum beacon light client, though in [#13031](https://github.com/paritytech/substrate/pull/13031)
|
||||
Parity team has wrapped some low level host functions for `bls-12381` but adding a high level host function specific
|
||||
for it is super helpful.
|
||||
|
||||
# Benchmark
|
||||
We add several benchmarks
|
||||
[here](https://github.com/Snowfork/snowbridge/blob/8891ca3cdcf2e04d8118c206588c956541ae4710/parachain/pallets/ethereum-beacon-client/src/benchmarking/mod.rs#L98-L124)
|
||||
as following to demonstrate
|
||||
[bls_fast_aggregate_verify](https://github.com/Snowfork/snowbridge/blob/8891ca3cdcf2e04d8118c206588c956541ae4710/parachain/pallets/ethereum-beacon-client/src/lib.rs#L764)
|
||||
is the main bottleneck. Test data
|
||||
[here](https://github.com/Snowfork/snowbridge/blob/8891ca3cdcf2e04d8118c206588c956541ae4710/parachain/pallets/ethereum-beacon-client/src/benchmarking/data_mainnet.rs#L553-L1120)
|
||||
is real from goerli network which contains 512 public keys from sync committee.
|
||||
|
||||
## sync_committee_period_update
|
||||
Base line benchmark for extrinsic [sync_committee_period_update](https://github.com/Snowfork/snowbridge/blob/8891ca3cdcf2e04d8118c206588c956541ae4710/parachain/pallets/ethereum-beacon-client/src/lib.rs#L233)
|
||||
|
||||
## bls_fast_aggregate_verify
|
||||
Subfunction of extrinsic `sync_committee_period_update` which does what
|
||||
[FastAggregateVerify](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-04#section-3.3.4) requires.
|
||||
|
||||
## bls_aggregate_pubkey
|
||||
Subfunction of `bls_fast_aggregate_verify` which decompress and instantiate G1 pubkeys only.
|
||||
|
||||
## bls_verify_message
|
||||
Subfunction of `bls_fast_aggregate_verify` which verify the prepared signature only.
|
||||
|
||||
|
||||
# Result
|
||||
|
||||
## hardware spec
|
||||
Run benchmark in a EC2 instance
|
||||
```
|
||||
cargo run --release --bin polkadot-parachain --features runtime-benchmarks -- benchmark machine --base-path /mnt/scratch/benchmark
|
||||
|
||||
+----------+----------------+-------------+-------------+-------------------+
|
||||
| Category | Function | Score | Minimum | Result |
|
||||
+===========================================================================+
|
||||
| CPU | BLAKE2-256 | 1.08 GiBs | 1.00 GiBs | ✅ Pass (107.5 %) |
|
||||
|----------+----------------+-------------+-------------+-------------------|
|
||||
| CPU | SR25519-Verify | 568.87 KiBs | 666.00 KiBs | ❌ Fail ( 85.4 %) |
|
||||
|----------+----------------+-------------+-------------+-------------------|
|
||||
| Memory | Copy | 13.67 GiBs | 14.32 GiBs | ✅ Pass ( 95.4 %) |
|
||||
|----------+----------------+-------------+-------------+-------------------|
|
||||
| Disk | Seq Write | 334.35 MiBs | 450.00 MiBs | ❌ Fail ( 74.3 %) |
|
||||
|----------+----------------+-------------+-------------+-------------------|
|
||||
| Disk | Rnd Write | 143.59 MiBs | 200.00 MiBs | ❌ Fail ( 71.8 %) |
|
||||
+----------+----------------+-------------+-------------+-------------------+
|
||||
```
|
||||
|
||||
## benchmark
|
||||
|
||||
```
|
||||
cargo run --release --bin polkadot-parachain \
|
||||
--features runtime-benchmarks \
|
||||
-- \
|
||||
benchmark pallet \
|
||||
--base-path /mnt/scratch/benchmark \
|
||||
--chain=bridge-hub-rococo-dev \
|
||||
--pallet=snowbridge_ethereum_beacon_client \
|
||||
--extrinsic="*" \
|
||||
--execution=wasm --wasm-execution=compiled \
|
||||
--steps 50 --repeat 20 \
|
||||
--output ./parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_ethereum_beacon_client.rs
|
||||
```
|
||||
|
||||
### [Weights](https://github.com/Snowfork/cumulus/blob/ron/benchmark-beacon-bridge/parachains/runtimes/bridge-hubs/bridge-hub-rococo/src/weights/snowbridge_ethereum_beacon_client.rs)
|
||||
|
||||
|extrinsic | minimum execution time benchmarked(us) |
|
||||
| --------------------------------------- |----------------------------------------|
|
||||
|sync_committee_period_update | 123_126 |
|
||||
|bls_fast_aggregate_verify| 121_083 |
|
||||
|bls_aggregate_pubkey | 90_306 |
|
||||
|bls_verify_message | 28_000 |
|
||||
|
||||
- [bls_fast_aggregate_verify](#bls_fast_aggregate_verify) consumes 98% execution time of [sync_committee_period_update](#sync_committee_period_update)
|
||||
|
||||
- [bls_aggregate_pubkey](#bls_aggregate_pubkey) consumes 75% execution time of [bls_fast_aggregate_verify](#bls_fast_aggregate_verify)
|
||||
|
||||
- [bls_verify_message](#bls_verify_message) consumes 23% execution time of [bls_fast_aggregate_verify](#bls_fast_aggregate_verify)
|
||||
|
||||
# Conclusion
|
||||
|
||||
A high level host function specific for
|
||||
[bls_fast_aggregate_verify](https://github.com/Snowfork/snowbridge/blob/8891ca3cdcf2e04d8118c206588c956541ae4710/parachain/pallets/ethereum-beacon-client/src/lib.rs#L764)
|
||||
is super helpful.
|
||||
+1215
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,156 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use super::*;
|
||||
|
||||
mod fixtures;
|
||||
mod util;
|
||||
|
||||
use crate::Pallet as EthereumBeaconClient;
|
||||
use frame_benchmarking::v2::*;
|
||||
use frame_system::RawOrigin;
|
||||
|
||||
use fixtures::{
|
||||
make_checkpoint, make_execution_header_update, make_finalized_header_update,
|
||||
make_sync_committee_update,
|
||||
};
|
||||
|
||||
use primitives::{
|
||||
fast_aggregate_verify, prepare_aggregate_pubkey, prepare_aggregate_signature,
|
||||
verify_merkle_branch,
|
||||
};
|
||||
use util::*;
|
||||
|
||||
#[benchmarks]
|
||||
mod benchmarks {
|
||||
use super::*;
|
||||
|
||||
#[benchmark]
|
||||
fn force_checkpoint() -> Result<(), BenchmarkError> {
|
||||
let checkpoint_update = make_checkpoint();
|
||||
let block_root: H256 = checkpoint_update.header.hash_tree_root().unwrap();
|
||||
|
||||
#[extrinsic_call]
|
||||
_(RawOrigin::Root, Box::new(*checkpoint_update));
|
||||
|
||||
assert!(<LatestFinalizedBlockRoot<T>>::get() == block_root);
|
||||
assert!(<FinalizedBeaconState<T>>::get(block_root).is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn submit() -> Result<(), BenchmarkError> {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let checkpoint_update = make_checkpoint();
|
||||
let finalized_header_update = make_finalized_header_update();
|
||||
let block_root: H256 = finalized_header_update.finalized_header.hash_tree_root().unwrap();
|
||||
EthereumBeaconClient::<T>::process_checkpoint_update(&checkpoint_update)?;
|
||||
|
||||
#[extrinsic_call]
|
||||
submit(RawOrigin::Signed(caller.clone()), Box::new(*finalized_header_update));
|
||||
|
||||
assert!(<LatestFinalizedBlockRoot<T>>::get() == block_root);
|
||||
assert!(<FinalizedBeaconState<T>>::get(block_root).is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark]
|
||||
fn submit_with_sync_committee() -> Result<(), BenchmarkError> {
|
||||
let caller: T::AccountId = whitelisted_caller();
|
||||
let checkpoint_update = make_checkpoint();
|
||||
let sync_committee_update = make_sync_committee_update();
|
||||
EthereumBeaconClient::<T>::process_checkpoint_update(&checkpoint_update)?;
|
||||
|
||||
#[extrinsic_call]
|
||||
submit(RawOrigin::Signed(caller.clone()), Box::new(*sync_committee_update));
|
||||
|
||||
assert!(<NextSyncCommittee<T>>::exists());
|
||||
|
||||
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())?;
|
||||
let update = make_sync_committee_update();
|
||||
let participant_pubkeys = participant_pubkeys::<T>(&update)?;
|
||||
let signing_root = signing_root::<T>(&update)?;
|
||||
let agg_sig =
|
||||
prepare_aggregate_signature(&update.sync_aggregate.sync_committee_signature).unwrap();
|
||||
let agg_pub_key = prepare_aggregate_pubkey(&participant_pubkeys).unwrap();
|
||||
|
||||
#[block]
|
||||
{
|
||||
agg_sig.fast_aggregate_verify_pre_aggregated(signing_root.as_bytes(), &agg_pub_key);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark(extra)]
|
||||
fn bls_fast_aggregate_verify() -> Result<(), BenchmarkError> {
|
||||
EthereumBeaconClient::<T>::process_checkpoint_update(&make_checkpoint())?;
|
||||
let update = make_sync_committee_update();
|
||||
let current_sync_committee = <CurrentSyncCommittee<T>>::get();
|
||||
let absent_pubkeys = absent_pubkeys::<T>(&update)?;
|
||||
let signing_root = signing_root::<T>(&update)?;
|
||||
|
||||
#[block]
|
||||
{
|
||||
fast_aggregate_verify(
|
||||
¤t_sync_committee.aggregate_pubkey,
|
||||
&absent_pubkeys,
|
||||
signing_root,
|
||||
&update.sync_aggregate.sync_committee_signature,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[benchmark(extra)]
|
||||
fn verify_merkle_proof() -> Result<(), BenchmarkError> {
|
||||
EthereumBeaconClient::<T>::process_checkpoint_update(&make_checkpoint())?;
|
||||
let update = make_sync_committee_update();
|
||||
let block_root: H256 = update.finalized_header.hash_tree_root().unwrap();
|
||||
|
||||
#[block]
|
||||
{
|
||||
verify_merkle_branch(
|
||||
block_root,
|
||||
&update.finality_branch,
|
||||
config::FINALIZED_ROOT_SUBTREE_INDEX,
|
||||
config::FINALIZED_ROOT_DEPTH,
|
||||
update.attested_header.state_root,
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl_benchmark_test_suite!(
|
||||
EthereumBeaconClient,
|
||||
crate::mock::mainnet::new_tester(),
|
||||
crate::mock::mainnet::Test
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use crate::{
|
||||
decompress_sync_committee_bits, Config, CurrentSyncCommittee, Pallet as EthereumBeaconClient,
|
||||
Update, ValidatorsRoot, Vec,
|
||||
};
|
||||
use primitives::PublicKeyPrepared;
|
||||
use sp_core::H256;
|
||||
|
||||
pub fn participant_pubkeys<T: Config>(
|
||||
update: &Update,
|
||||
) -> Result<Vec<PublicKeyPrepared>, &'static str> {
|
||||
let sync_committee_bits =
|
||||
decompress_sync_committee_bits(update.sync_aggregate.sync_committee_bits);
|
||||
let current_sync_committee = <CurrentSyncCommittee<T>>::get();
|
||||
let pubkeys = EthereumBeaconClient::<T>::find_pubkeys(
|
||||
&sync_committee_bits,
|
||||
(*current_sync_committee.pubkeys).as_ref(),
|
||||
true,
|
||||
);
|
||||
Ok(pubkeys)
|
||||
}
|
||||
|
||||
pub fn absent_pubkeys<T: Config>(update: &Update) -> Result<Vec<PublicKeyPrepared>, &'static str> {
|
||||
let sync_committee_bits =
|
||||
decompress_sync_committee_bits(update.sync_aggregate.sync_committee_bits);
|
||||
let current_sync_committee = <CurrentSyncCommittee<T>>::get();
|
||||
let pubkeys = EthereumBeaconClient::<T>::find_pubkeys(
|
||||
&sync_committee_bits,
|
||||
(*current_sync_committee.pubkeys).as_ref(),
|
||||
false,
|
||||
);
|
||||
Ok(pubkeys)
|
||||
}
|
||||
|
||||
pub fn signing_root<T: Config>(update: &Update) -> Result<H256, &'static str> {
|
||||
let validators_root = <ValidatorsRoot<T>>::get();
|
||||
let signing_root = EthereumBeaconClient::<T>::signing_root(
|
||||
&update.attested_header,
|
||||
validators_root,
|
||||
update.signature_slot,
|
||||
)?;
|
||||
Ok(signing_root)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
pub const SLOTS_PER_EPOCH: usize = 32;
|
||||
pub const SECONDS_PER_SLOT: usize = 12;
|
||||
pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: usize = 256;
|
||||
pub const SYNC_COMMITTEE_SIZE: usize = 512;
|
||||
pub const SYNC_COMMITTEE_BITS_SIZE: usize = SYNC_COMMITTEE_SIZE / 8;
|
||||
pub const SLOTS_PER_HISTORICAL_ROOT: usize = 8192;
|
||||
pub const IS_MINIMAL: bool = false;
|
||||
pub const BLOCK_ROOT_AT_INDEX_DEPTH: usize = 13;
|
||||
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
pub const SLOTS_PER_EPOCH: usize = 8;
|
||||
pub const SECONDS_PER_SLOT: usize = 6;
|
||||
pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: usize = 8;
|
||||
pub const SYNC_COMMITTEE_SIZE: usize = 32;
|
||||
pub const SYNC_COMMITTEE_BITS_SIZE: usize = SYNC_COMMITTEE_SIZE / 8;
|
||||
pub const SLOTS_PER_HISTORICAL_ROOT: usize = 64;
|
||||
pub const IS_MINIMAL: bool = true;
|
||||
pub const BLOCK_ROOT_AT_INDEX_DEPTH: usize = 6;
|
||||
@@ -0,0 +1,56 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use primitives::merkle_proof::{generalized_index_length, subtree_index};
|
||||
use static_assertions::const_assert;
|
||||
|
||||
pub mod mainnet;
|
||||
pub mod minimal;
|
||||
|
||||
#[cfg(not(feature = "beacon-spec-mainnet"))]
|
||||
pub use minimal::*;
|
||||
|
||||
#[cfg(feature = "beacon-spec-mainnet")]
|
||||
pub use mainnet::*;
|
||||
|
||||
// Generalized Indices
|
||||
|
||||
// get_generalized_index(BeaconState, 'block_roots')
|
||||
pub const BLOCK_ROOTS_INDEX: usize = 37;
|
||||
pub const BLOCK_ROOTS_SUBTREE_INDEX: usize = subtree_index(BLOCK_ROOTS_INDEX);
|
||||
pub const BLOCK_ROOTS_DEPTH: usize = generalized_index_length(BLOCK_ROOTS_INDEX);
|
||||
|
||||
// get_generalized_index(BeaconState, 'finalized_checkpoint', 'root')
|
||||
pub const FINALIZED_ROOT_INDEX: usize = 105;
|
||||
pub const FINALIZED_ROOT_SUBTREE_INDEX: usize = subtree_index(FINALIZED_ROOT_INDEX);
|
||||
pub const FINALIZED_ROOT_DEPTH: usize = generalized_index_length(FINALIZED_ROOT_INDEX);
|
||||
|
||||
// get_generalized_index(BeaconState, 'current_sync_committee')
|
||||
pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54;
|
||||
pub const CURRENT_SYNC_COMMITTEE_SUBTREE_INDEX: usize = subtree_index(CURRENT_SYNC_COMMITTEE_INDEX);
|
||||
pub const CURRENT_SYNC_COMMITTEE_DEPTH: usize =
|
||||
generalized_index_length(CURRENT_SYNC_COMMITTEE_INDEX);
|
||||
|
||||
// get_generalized_index(BeaconState, 'next_sync_committee')
|
||||
pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55;
|
||||
pub const NEXT_SYNC_COMMITTEE_SUBTREE_INDEX: usize = subtree_index(NEXT_SYNC_COMMITTEE_INDEX);
|
||||
pub const NEXT_SYNC_COMMITTEE_DEPTH: usize = generalized_index_length(NEXT_SYNC_COMMITTEE_INDEX);
|
||||
|
||||
// get_generalized_index(BeaconBlockBody, 'execution_payload')
|
||||
pub const EXECUTION_HEADER_INDEX: usize = 25;
|
||||
pub const EXECUTION_HEADER_SUBTREE_INDEX: usize = subtree_index(EXECUTION_HEADER_INDEX);
|
||||
pub const EXECUTION_HEADER_DEPTH: usize = generalized_index_length(EXECUTION_HEADER_INDEX);
|
||||
|
||||
pub const MAX_EXTRA_DATA_BYTES: usize = 32;
|
||||
pub const MAX_LOGS_BLOOM_SIZE: usize = 256;
|
||||
pub const MAX_FEE_RECIPIENT_SIZE: usize = 20;
|
||||
|
||||
pub const MAX_BRANCH_PROOF_SIZE: usize = 20;
|
||||
|
||||
/// DomainType('0x07000000')
|
||||
/// <https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/beacon-chain.md#domain-types>
|
||||
pub const DOMAIN_SYNC_COMMITTEE: [u8; 4] = [7, 0, 0, 0];
|
||||
|
||||
pub const PUBKEY_SIZE: usize = 48;
|
||||
pub const SIGNATURE_SIZE: usize = 96;
|
||||
|
||||
const_assert!(SYNC_COMMITTEE_BITS_SIZE == SYNC_COMMITTEE_SIZE / 8);
|
||||
@@ -0,0 +1,31 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use crate::config::{
|
||||
EPOCHS_PER_SYNC_COMMITTEE_PERIOD, SLOTS_PER_EPOCH, SYNC_COMMITTEE_BITS_SIZE,
|
||||
SYNC_COMMITTEE_SIZE,
|
||||
};
|
||||
|
||||
/// Decompress packed bitvector into byte vector according to SSZ deserialization rules. Each byte
|
||||
/// in the decompressed vector is either 0 or 1.
|
||||
pub fn decompress_sync_committee_bits(
|
||||
input: [u8; SYNC_COMMITTEE_BITS_SIZE],
|
||||
) -> [u8; SYNC_COMMITTEE_SIZE] {
|
||||
primitives::decompress_sync_committee_bits::<SYNC_COMMITTEE_SIZE, SYNC_COMMITTEE_BITS_SIZE>(
|
||||
input,
|
||||
)
|
||||
}
|
||||
|
||||
/// Compute the sync committee period in which a slot is contained.
|
||||
pub fn compute_period(slot: u64) -> u64 {
|
||||
slot / SLOTS_PER_EPOCH as u64 / EPOCHS_PER_SYNC_COMMITTEE_PERIOD as u64
|
||||
}
|
||||
|
||||
/// Compute epoch in which a slot is contained.
|
||||
pub fn compute_epoch(slot: u64, slots_per_epoch: u64) -> u64 {
|
||||
slot / slots_per_epoch
|
||||
}
|
||||
|
||||
/// Sums the bit vector of sync committee participation.
|
||||
pub fn sync_committee_sum(sync_committee_bits: &[u8]) -> u32 {
|
||||
sync_committee_bits.iter().fold(0, |acc: u32, x| acc + *x as u32)
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use super::*;
|
||||
|
||||
use snowbridge_core::inbound::{
|
||||
VerificationError::{self, *},
|
||||
*,
|
||||
};
|
||||
use snowbridge_ethereum::Receipt;
|
||||
|
||||
impl<T: Config> Verifier for Pallet<T> {
|
||||
/// Verify a message by verifying the existence of the corresponding
|
||||
/// Ethereum log in a block. Returns the log if successful. The execution header containing
|
||||
/// 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-beacon-client",
|
||||
"💫 Verifying message with block hash {}",
|
||||
proof.block_hash,
|
||||
);
|
||||
|
||||
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-beacon-client",
|
||||
"💫 Verification of receipt inclusion failed for block {}: {:?}",
|
||||
proof.block_hash,
|
||||
err
|
||||
);
|
||||
return Err(err)
|
||||
},
|
||||
};
|
||||
|
||||
log::trace!(
|
||||
target: "ethereum-beacon-client",
|
||||
"💫 Verified receipt inclusion for transaction at index {} in block {}",
|
||||
proof.tx_index, proof.block_hash,
|
||||
);
|
||||
|
||||
event_log.validate().map_err(|_| InvalidLog)?;
|
||||
|
||||
// Convert snowbridge_core::inbound::Log to snowbridge_ethereum::Log.
|
||||
let event_log = snowbridge_ethereum::Log {
|
||||
address: event_log.address,
|
||||
topics: event_log.topics.clone(),
|
||||
data: event_log.data.clone(),
|
||||
};
|
||||
|
||||
if !receipt.contains_log(&event_log) {
|
||||
log::error!(
|
||||
target: "ethereum-beacon-client",
|
||||
"💫 Event log not found in receipt for transaction at index {} in block {}",
|
||||
proof.tx_index, proof.block_hash,
|
||||
);
|
||||
return Err(LogNotFound)
|
||||
}
|
||||
|
||||
log::info!(
|
||||
target: "ethereum-beacon-client",
|
||||
"💫 Receipt verification successful for {}",
|
||||
proof.block_hash,
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Verifies that the receipt encoded in `proof.data` is included in the block given by
|
||||
/// `proof.block_hash`.
|
||||
pub fn verify_receipt_inclusion(
|
||||
receipts_root: H256,
|
||||
proof: &Proof,
|
||||
) -> Result<Receipt, VerificationError> {
|
||||
let result = verify_receipt_proof(receipts_root, &proof.data.1).ok_or(InvalidProof)?;
|
||||
|
||||
match result {
|
||||
Ok(receipt) => Ok(receipt),
|
||||
Err(err) => {
|
||||
log::trace!(
|
||||
target: "ethereum-beacon-client",
|
||||
"💫 Failed to decode transaction receipt: {}",
|
||||
err
|
||||
);
|
||||
Err(InvalidProof)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,841 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
//! Ethereum Beacon Client
|
||||
//!
|
||||
//! A light client that verifies consensus updates signed by the sync committee of the beacon chain.
|
||||
//!
|
||||
//! # Extrinsics
|
||||
//!
|
||||
//! ## Governance
|
||||
//!
|
||||
//! * [`Call::force_checkpoint`]: Set the initial trusted consensus checkpoint.
|
||||
//! * [`Call::set_operating_mode`]: Set the operating mode of the pallet. Can be used to disable
|
||||
//! processing of conensus updates.
|
||||
//!
|
||||
//! ## Consensus Updates
|
||||
//!
|
||||
//! * [`Call::submit`]: Submit a finalized beacon header with an optional sync committee update
|
||||
//! * [`Call::submit_execution_header`]: Submit an execution header together with an ancestry proof
|
||||
//! that can be verified against an already imported finalized beacon header.
|
||||
#![cfg_attr(not(feature = "std"), no_std)]
|
||||
|
||||
pub mod config;
|
||||
pub mod functions;
|
||||
pub mod impls;
|
||||
pub mod types;
|
||||
pub mod weights;
|
||||
|
||||
#[cfg(any(test, feature = "fuzzing"))]
|
||||
pub mod mock;
|
||||
|
||||
#[cfg(all(test, not(feature = "beacon-spec-mainnet")))]
|
||||
mod tests;
|
||||
|
||||
#[cfg(feature = "runtime-benchmarks")]
|
||||
mod benchmarking;
|
||||
|
||||
use frame_support::{
|
||||
dispatch::DispatchResult, pallet_prelude::OptionQuery, traits::Get, transactional,
|
||||
};
|
||||
use frame_system::ensure_signed;
|
||||
use primitives::{
|
||||
fast_aggregate_verify, verify_merkle_branch, verify_receipt_proof, BeaconHeader, BlsError,
|
||||
CompactBeaconState, CompactExecutionHeader, ExecutionHeaderState, ForkData, ForkVersion,
|
||||
ForkVersions, PublicKeyPrepared, SigningData,
|
||||
};
|
||||
use snowbridge_core::{BasicOperatingMode, RingBufferMap};
|
||||
use sp_core::H256;
|
||||
use sp_std::prelude::*;
|
||||
pub use weights::WeightInfo;
|
||||
|
||||
use functions::{
|
||||
compute_epoch, compute_period, decompress_sync_committee_bits, sync_committee_sum,
|
||||
};
|
||||
pub use types::ExecutionHeaderBuffer;
|
||||
use types::{
|
||||
CheckpointUpdate, ExecutionHeaderUpdate, FinalizedBeaconStateBuffer, SyncCommitteePrepared,
|
||||
Update,
|
||||
};
|
||||
|
||||
pub use pallet::*;
|
||||
|
||||
pub use config::SLOTS_PER_HISTORICAL_ROOT;
|
||||
|
||||
pub const LOG_TARGET: &str = "ethereum-beacon-client";
|
||||
|
||||
#[frame_support::pallet]
|
||||
pub mod pallet {
|
||||
use super::*;
|
||||
|
||||
use frame_support::pallet_prelude::*;
|
||||
use frame_system::pallet_prelude::*;
|
||||
|
||||
#[derive(scale_info::TypeInfo, codec::Encode, codec::Decode, codec::MaxEncodedLen)]
|
||||
#[codec(mel_bound(T: Config))]
|
||||
#[scale_info(skip_type_params(T))]
|
||||
pub struct MaxFinalizedHeadersToKeep<T: Config>(PhantomData<T>);
|
||||
impl<T: Config> Get<u32> for MaxFinalizedHeadersToKeep<T> {
|
||||
fn get() -> u32 {
|
||||
// Consider max latency allowed between LatestFinalizedState and LatestExecutionState is
|
||||
// the total slots in one sync_committee_period so 1 should be fine we keep 2 periods
|
||||
// here for redundancy.
|
||||
const MAX_REDUNDANCY: u32 = 2;
|
||||
config::EPOCHS_PER_SYNC_COMMITTEE_PERIOD as u32 * MAX_REDUNDANCY
|
||||
}
|
||||
}
|
||||
|
||||
#[pallet::pallet]
|
||||
pub struct Pallet<T>(_);
|
||||
|
||||
#[pallet::config]
|
||||
pub trait Config: frame_system::Config {
|
||||
type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
|
||||
#[pallet::constant]
|
||||
type ForkVersions: Get<ForkVersions>;
|
||||
/// Maximum number of execution headers to keep
|
||||
#[pallet::constant]
|
||||
type MaxExecutionHeadersToKeep: Get<u32>;
|
||||
type WeightInfo: WeightInfo;
|
||||
}
|
||||
|
||||
#[pallet::event]
|
||||
#[pallet::generate_deposit(pub(super) fn deposit_event)]
|
||||
pub enum Event<T: Config> {
|
||||
BeaconHeaderImported {
|
||||
block_hash: H256,
|
||||
slot: u64,
|
||||
},
|
||||
ExecutionHeaderImported {
|
||||
block_hash: H256,
|
||||
block_number: u64,
|
||||
},
|
||||
SyncCommitteeUpdated {
|
||||
period: u64,
|
||||
},
|
||||
/// Set OperatingMode
|
||||
OperatingModeChanged {
|
||||
mode: BasicOperatingMode,
|
||||
},
|
||||
}
|
||||
|
||||
#[pallet::error]
|
||||
pub enum Error<T> {
|
||||
SkippedSyncCommitteePeriod,
|
||||
/// Attested header is older than latest finalized header.
|
||||
IrrelevantUpdate,
|
||||
NotBootstrapped,
|
||||
SyncCommitteeParticipantsNotSupermajority,
|
||||
InvalidHeaderMerkleProof,
|
||||
InvalidSyncCommitteeMerkleProof,
|
||||
InvalidExecutionHeaderProof,
|
||||
InvalidAncestryMerkleProof,
|
||||
InvalidBlockRootsRootMerkleProof,
|
||||
HeaderNotFinalized,
|
||||
BlockBodyHashTreeRootFailed,
|
||||
HeaderHashTreeRootFailed,
|
||||
SyncCommitteeHashTreeRootFailed,
|
||||
SigningRootHashTreeRootFailed,
|
||||
ForkDataHashTreeRootFailed,
|
||||
ExpectedFinalizedHeaderNotStored,
|
||||
BLSPreparePublicKeysFailed,
|
||||
BLSVerificationFailed(BlsError),
|
||||
InvalidUpdateSlot,
|
||||
/// The given update is not in the expected period, or the given next sync committee does
|
||||
/// not match the next sync committee in storage.
|
||||
InvalidSyncCommitteeUpdate,
|
||||
ExecutionHeaderTooFarBehind,
|
||||
ExecutionHeaderSkippedBlock,
|
||||
Halted,
|
||||
}
|
||||
|
||||
/// Latest imported checkpoint root
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn initial_checkpoint_root)]
|
||||
pub(super) type InitialCheckpointRoot<T: Config> = StorageValue<_, H256, ValueQuery>;
|
||||
|
||||
/// Latest imported finalized block root
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn latest_finalized_block_root)]
|
||||
pub(super) type LatestFinalizedBlockRoot<T: Config> = StorageValue<_, H256, ValueQuery>;
|
||||
|
||||
/// Beacon state by finalized block root
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn finalized_beacon_state)]
|
||||
pub(super) type FinalizedBeaconState<T: Config> =
|
||||
StorageMap<_, Identity, H256, CompactBeaconState, OptionQuery>;
|
||||
|
||||
/// Finalized Headers: Current position in ring buffer
|
||||
#[pallet::storage]
|
||||
pub(crate) type FinalizedBeaconStateIndex<T: Config> = StorageValue<_, u32, ValueQuery>;
|
||||
|
||||
/// Finalized Headers: Mapping of ring buffer index to a pruning candidate
|
||||
#[pallet::storage]
|
||||
pub(crate) type FinalizedBeaconStateMapping<T: Config> =
|
||||
StorageMap<_, Identity, u32, H256, ValueQuery>;
|
||||
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn validators_root)]
|
||||
pub(super) type ValidatorsRoot<T: Config> = StorageValue<_, H256, ValueQuery>;
|
||||
|
||||
/// Sync committee for current period
|
||||
#[pallet::storage]
|
||||
pub(super) type CurrentSyncCommittee<T: Config> =
|
||||
StorageValue<_, SyncCommitteePrepared, ValueQuery>;
|
||||
|
||||
/// Sync committee for next period
|
||||
#[pallet::storage]
|
||||
pub(super) type NextSyncCommittee<T: Config> =
|
||||
StorageValue<_, SyncCommitteePrepared, ValueQuery>;
|
||||
|
||||
/// Latest imported execution header
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn latest_execution_state)]
|
||||
pub(super) type LatestExecutionState<T: Config> =
|
||||
StorageValue<_, ExecutionHeaderState, ValueQuery>;
|
||||
|
||||
/// Execution Headers
|
||||
#[pallet::storage]
|
||||
pub type ExecutionHeaders<T: Config> =
|
||||
StorageMap<_, Identity, H256, CompactExecutionHeader, OptionQuery>;
|
||||
|
||||
/// Execution Headers: Current position in ring buffer
|
||||
#[pallet::storage]
|
||||
pub type ExecutionHeaderIndex<T: Config> = StorageValue<_, u32, ValueQuery>;
|
||||
|
||||
/// Execution Headers: Mapping of ring buffer index to a pruning candidate
|
||||
#[pallet::storage]
|
||||
pub type ExecutionHeaderMapping<T: Config> = StorageMap<_, Identity, u32, H256, ValueQuery>;
|
||||
|
||||
/// The current operating mode of the pallet.
|
||||
#[pallet::storage]
|
||||
#[pallet::getter(fn operating_mode)]
|
||||
pub type OperatingMode<T: Config> = StorageValue<_, BasicOperatingMode, ValueQuery>;
|
||||
|
||||
#[pallet::call]
|
||||
impl<T: Config> Pallet<T> {
|
||||
#[pallet::call_index(0)]
|
||||
#[pallet::weight(T::WeightInfo::force_checkpoint())]
|
||||
#[transactional]
|
||||
/// Used for pallet initialization and light client resetting. Needs to be called by
|
||||
/// the root origin.
|
||||
pub fn force_checkpoint(
|
||||
origin: OriginFor<T>,
|
||||
update: Box<CheckpointUpdate>,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
Self::process_checkpoint_update(&update)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pallet::call_index(1)]
|
||||
#[pallet::weight({
|
||||
match update.next_sync_committee_update {
|
||||
None => T::WeightInfo::submit(),
|
||||
Some(_) => T::WeightInfo::submit_with_sync_committee(),
|
||||
}
|
||||
})]
|
||||
#[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 {
|
||||
ensure_signed(origin)?;
|
||||
ensure!(!Self::operating_mode().is_halted(), Error::<T>::Halted);
|
||||
Self::process_update(&update)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pallet::call_index(2)]
|
||||
#[pallet::weight(T::WeightInfo::submit_execution_header())]
|
||||
#[transactional]
|
||||
/// Submits a new execution header update. The relevant related beacon header
|
||||
/// is also included to prove the execution header, as well as ancestry proof data.
|
||||
pub fn submit_execution_header(
|
||||
origin: OriginFor<T>,
|
||||
update: Box<ExecutionHeaderUpdate>,
|
||||
) -> DispatchResult {
|
||||
ensure_signed(origin)?;
|
||||
ensure!(!Self::operating_mode().is_halted(), Error::<T>::Halted);
|
||||
Self::process_execution_header_update(&update)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Halt or resume all pallet operations. May only be called by root.
|
||||
#[pallet::call_index(3)]
|
||||
#[pallet::weight((T::DbWeight::get().reads_writes(1, 1), DispatchClass::Operational))]
|
||||
pub fn set_operating_mode(
|
||||
origin: OriginFor<T>,
|
||||
mode: BasicOperatingMode,
|
||||
) -> DispatchResult {
|
||||
ensure_root(origin)?;
|
||||
OperatingMode::<T>::set(mode);
|
||||
Self::deposit_event(Event::OperatingModeChanged { mode });
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Config> Pallet<T> {
|
||||
/// Forces a finalized beacon header checkpoint update. The current sync committee,
|
||||
/// with a header attesting to the current sync committee, should be provided.
|
||||
/// An `block_roots` proof should also be provided. This is used for ancestry proofs
|
||||
/// for execution header updates.
|
||||
pub(crate) fn process_checkpoint_update(update: &CheckpointUpdate) -> DispatchResult {
|
||||
let sync_committee_root = update
|
||||
.current_sync_committee
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::SyncCommitteeHashTreeRootFailed)?;
|
||||
|
||||
// Verifies the sync committee in the Beacon state.
|
||||
ensure!(
|
||||
verify_merkle_branch(
|
||||
sync_committee_root,
|
||||
&update.current_sync_committee_branch,
|
||||
config::CURRENT_SYNC_COMMITTEE_SUBTREE_INDEX,
|
||||
config::CURRENT_SYNC_COMMITTEE_DEPTH,
|
||||
update.header.state_root
|
||||
),
|
||||
Error::<T>::InvalidSyncCommitteeMerkleProof
|
||||
);
|
||||
|
||||
let header_root: H256 = update
|
||||
.header
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;
|
||||
|
||||
// This is used for ancestry proofs in ExecutionHeader updates. This verifies the
|
||||
// BeaconState: the beacon state root is the tree root; the `block_roots` hash is the
|
||||
// tree leaf.
|
||||
ensure!(
|
||||
verify_merkle_branch(
|
||||
update.block_roots_root,
|
||||
&update.block_roots_branch,
|
||||
config::BLOCK_ROOTS_SUBTREE_INDEX,
|
||||
config::BLOCK_ROOTS_DEPTH,
|
||||
update.header.state_root
|
||||
),
|
||||
Error::<T>::InvalidBlockRootsRootMerkleProof
|
||||
);
|
||||
|
||||
let sync_committee_prepared: SyncCommitteePrepared = (&update.current_sync_committee)
|
||||
.try_into()
|
||||
.map_err(|_| <Error<T>>::BLSPreparePublicKeysFailed)?;
|
||||
<CurrentSyncCommittee<T>>::set(sync_committee_prepared);
|
||||
<NextSyncCommittee<T>>::kill();
|
||||
InitialCheckpointRoot::<T>::set(header_root);
|
||||
<LatestExecutionState<T>>::kill();
|
||||
|
||||
Self::store_validators_root(update.validators_root);
|
||||
Self::store_finalized_header(header_root, update.header, update.block_roots_root)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn process_update(update: &Update) -> DispatchResult {
|
||||
Self::cross_check_execution_state()?;
|
||||
Self::verify_update(update)?;
|
||||
Self::apply_update(update)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Cross check to make sure that execution header import does not fall too far behind
|
||||
/// finalised beacon header import. If that happens just return an error and pause
|
||||
/// processing until execution header processing has caught up.
|
||||
pub(crate) fn cross_check_execution_state() -> DispatchResult {
|
||||
let latest_finalized_state =
|
||||
FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get())
|
||||
.ok_or(Error::<T>::NotBootstrapped)?;
|
||||
let latest_execution_state = Self::latest_execution_state();
|
||||
// The execution header import should be at least within the slot range of a sync
|
||||
// committee period.
|
||||
let max_latency = config::EPOCHS_PER_SYNC_COMMITTEE_PERIOD * config::SLOTS_PER_EPOCH;
|
||||
ensure!(
|
||||
latest_execution_state.beacon_slot == 0 ||
|
||||
latest_finalized_state.slot <
|
||||
latest_execution_state.beacon_slot + max_latency as u64,
|
||||
Error::<T>::ExecutionHeaderTooFarBehind
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// References and strictly follows <https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md#validate_light_client_update>
|
||||
/// Verifies that provided next sync committee is valid through a series of checks
|
||||
/// (including checking that a sync committee period isn't skipped and that the header is
|
||||
/// signed by the current sync committee.
|
||||
fn verify_update(update: &Update) -> DispatchResult {
|
||||
// Verify sync committee has sufficient participants.
|
||||
let participation =
|
||||
decompress_sync_committee_bits(update.sync_aggregate.sync_committee_bits);
|
||||
Self::sync_committee_participation_is_supermajority(&participation)?;
|
||||
|
||||
// Verify update does not skip a sync committee period.
|
||||
ensure!(
|
||||
update.signature_slot > update.attested_header.slot &&
|
||||
update.attested_header.slot >= update.finalized_header.slot,
|
||||
Error::<T>::InvalidUpdateSlot
|
||||
);
|
||||
// Retrieve latest finalized state.
|
||||
let latest_finalized_state =
|
||||
FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get())
|
||||
.ok_or(Error::<T>::NotBootstrapped)?;
|
||||
let store_period = compute_period(latest_finalized_state.slot);
|
||||
let signature_period = compute_period(update.signature_slot);
|
||||
if <NextSyncCommittee<T>>::exists() {
|
||||
ensure!(
|
||||
(store_period..=store_period + 1).contains(&signature_period),
|
||||
Error::<T>::SkippedSyncCommitteePeriod
|
||||
)
|
||||
} else {
|
||||
ensure!(signature_period == store_period, Error::<T>::SkippedSyncCommitteePeriod)
|
||||
}
|
||||
|
||||
// Verify update is relevant.
|
||||
let update_attested_period = compute_period(update.attested_header.slot);
|
||||
let update_has_next_sync_committee = !<NextSyncCommittee<T>>::exists() &&
|
||||
(update.next_sync_committee_update.is_some() &&
|
||||
update_attested_period == store_period);
|
||||
ensure!(
|
||||
update.attested_header.slot > latest_finalized_state.slot ||
|
||||
update_has_next_sync_committee,
|
||||
Error::<T>::IrrelevantUpdate
|
||||
);
|
||||
|
||||
// Verify that the `finality_branch`, if present, confirms `finalized_header` to match
|
||||
// the finalized checkpoint root saved in the state of `attested_header`.
|
||||
let finalized_block_root: H256 = update
|
||||
.finalized_header
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;
|
||||
ensure!(
|
||||
verify_merkle_branch(
|
||||
finalized_block_root,
|
||||
&update.finality_branch,
|
||||
config::FINALIZED_ROOT_SUBTREE_INDEX,
|
||||
config::FINALIZED_ROOT_DEPTH,
|
||||
update.attested_header.state_root
|
||||
),
|
||||
Error::<T>::InvalidHeaderMerkleProof
|
||||
);
|
||||
|
||||
// Though following check does not belong to ALC spec we verify block_roots_root to
|
||||
// match the finalized checkpoint root saved in the state of `finalized_header` so to
|
||||
// cache it for later use in `verify_ancestry_proof`.
|
||||
ensure!(
|
||||
verify_merkle_branch(
|
||||
update.block_roots_root,
|
||||
&update.block_roots_branch,
|
||||
config::BLOCK_ROOTS_SUBTREE_INDEX,
|
||||
config::BLOCK_ROOTS_DEPTH,
|
||||
update.finalized_header.state_root
|
||||
),
|
||||
Error::<T>::InvalidBlockRootsRootMerkleProof
|
||||
);
|
||||
|
||||
// Verify that the `next_sync_committee`, if present, actually is the next sync
|
||||
// committee saved in the state of the `attested_header`.
|
||||
if let Some(next_sync_committee_update) = &update.next_sync_committee_update {
|
||||
let sync_committee_root = next_sync_committee_update
|
||||
.next_sync_committee
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::SyncCommitteeHashTreeRootFailed)?;
|
||||
if update_attested_period == store_period && <NextSyncCommittee<T>>::exists() {
|
||||
let next_committee_root = <NextSyncCommittee<T>>::get().root;
|
||||
ensure!(
|
||||
sync_committee_root == next_committee_root,
|
||||
Error::<T>::InvalidSyncCommitteeUpdate
|
||||
);
|
||||
}
|
||||
ensure!(
|
||||
verify_merkle_branch(
|
||||
sync_committee_root,
|
||||
&next_sync_committee_update.next_sync_committee_branch,
|
||||
config::NEXT_SYNC_COMMITTEE_SUBTREE_INDEX,
|
||||
config::NEXT_SYNC_COMMITTEE_DEPTH,
|
||||
update.attested_header.state_root
|
||||
),
|
||||
Error::<T>::InvalidSyncCommitteeMerkleProof
|
||||
);
|
||||
}
|
||||
|
||||
// Verify sync committee aggregate signature.
|
||||
let sync_committee = if signature_period == store_period {
|
||||
<CurrentSyncCommittee<T>>::get()
|
||||
} else {
|
||||
<NextSyncCommittee<T>>::get()
|
||||
};
|
||||
let absent_pubkeys =
|
||||
Self::find_pubkeys(&participation, (*sync_committee.pubkeys).as_ref(), false);
|
||||
let signing_root = Self::signing_root(
|
||||
&update.attested_header,
|
||||
Self::validators_root(),
|
||||
update.signature_slot,
|
||||
)?;
|
||||
// Improvement here per <https://eth2book.info/capella/part2/building_blocks/signatures/#sync-aggregates>
|
||||
// suggested start from the full set aggregate_pubkey then subtracting the absolute
|
||||
// minority that did not participate.
|
||||
fast_aggregate_verify(
|
||||
&sync_committee.aggregate_pubkey,
|
||||
&absent_pubkeys,
|
||||
signing_root,
|
||||
&update.sync_aggregate.sync_committee_signature,
|
||||
)
|
||||
.map_err(|e| Error::<T>::BLSVerificationFailed(e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
let latest_finalized_state =
|
||||
FinalizedBeaconState::<T>::get(LatestFinalizedBlockRoot::<T>::get())
|
||||
.ok_or(Error::<T>::NotBootstrapped)?;
|
||||
if let Some(next_sync_committee_update) = &update.next_sync_committee_update {
|
||||
let store_period = compute_period(latest_finalized_state.slot);
|
||||
let update_finalized_period = compute_period(update.finalized_header.slot);
|
||||
let sync_committee_prepared: SyncCommitteePrepared = (&next_sync_committee_update
|
||||
.next_sync_committee)
|
||||
.try_into()
|
||||
.map_err(|_| <Error<T>>::BLSPreparePublicKeysFailed)?;
|
||||
|
||||
if !<NextSyncCommittee<T>>::exists() {
|
||||
ensure!(
|
||||
update_finalized_period == store_period,
|
||||
<Error<T>>::InvalidSyncCommitteeUpdate
|
||||
);
|
||||
<NextSyncCommittee<T>>::set(sync_committee_prepared);
|
||||
} else if update_finalized_period == store_period + 1 {
|
||||
<CurrentSyncCommittee<T>>::set(<NextSyncCommittee<T>>::get());
|
||||
<NextSyncCommittee<T>>::set(sync_committee_prepared);
|
||||
}
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"💫 SyncCommitteeUpdated at period {}.",
|
||||
update_finalized_period
|
||||
);
|
||||
Self::deposit_event(Event::SyncCommitteeUpdated {
|
||||
period: update_finalized_period,
|
||||
});
|
||||
};
|
||||
|
||||
if update.finalized_header.slot > latest_finalized_state.slot {
|
||||
let finalized_block_root: H256 = update
|
||||
.finalized_header
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;
|
||||
Self::store_finalized_header(
|
||||
finalized_block_root,
|
||||
update.finalized_header,
|
||||
update.block_roots_root,
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Validates an execution header for import. 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 process_execution_header_update(
|
||||
update: &ExecutionHeaderUpdate,
|
||||
) -> 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!(
|
||||
update.header.slot <= latest_finalized_state.slot,
|
||||
Error::<T>::HeaderNotFinalized
|
||||
);
|
||||
|
||||
// Checks that we don't skip execution headers, they need to be imported sequentially.
|
||||
let latest_execution_state: ExecutionHeaderState = Self::latest_execution_state();
|
||||
ensure!(
|
||||
latest_execution_state.block_number == 0 ||
|
||||
update.execution_header.block_number ==
|
||||
latest_execution_state.block_number + 1,
|
||||
Error::<T>::ExecutionHeaderSkippedBlock
|
||||
);
|
||||
|
||||
// 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 = update
|
||||
.execution_header
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::BlockBodyHashTreeRootFailed)?;
|
||||
|
||||
ensure!(
|
||||
verify_merkle_branch(
|
||||
execution_header_root,
|
||||
&update.execution_branch,
|
||||
config::EXECUTION_HEADER_SUBTREE_INDEX,
|
||||
config::EXECUTION_HEADER_DEPTH,
|
||||
update.header.body_root
|
||||
),
|
||||
Error::<T>::InvalidExecutionHeaderProof
|
||||
);
|
||||
|
||||
let block_root: H256 = update
|
||||
.header
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;
|
||||
|
||||
match &update.ancestry_proof {
|
||||
Some(proof) => {
|
||||
Self::verify_ancestry_proof(
|
||||
block_root,
|
||||
update.header.slot,
|
||||
&proof.header_branch,
|
||||
proof.finalized_block_root,
|
||||
)?;
|
||||
},
|
||||
None => {
|
||||
// If the ancestry proof is not provided, we expect this header to be a
|
||||
// finalized header. We need to check that the header hash matches the finalized
|
||||
// header root at the expected slot.
|
||||
let state = <FinalizedBeaconState<T>>::get(block_root)
|
||||
.ok_or(Error::<T>::ExpectedFinalizedHeaderNotStored)?;
|
||||
if update.header.slot != state.slot {
|
||||
return Err(Error::<T>::ExpectedFinalizedHeaderNotStored.into())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Self::store_execution_header(
|
||||
update.execution_header.block_hash,
|
||||
update.execution_header.clone().into(),
|
||||
update.header.slot,
|
||||
block_root,
|
||||
);
|
||||
|
||||
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(())
|
||||
}
|
||||
|
||||
/// Computes the signing root for a given beacon header and domain. The hash tree root
|
||||
/// of the beacon header is computed, and then the combination of the beacon header hash
|
||||
/// and the domain makes up the signing root.
|
||||
pub(super) fn compute_signing_root(
|
||||
beacon_header: &BeaconHeader,
|
||||
domain: H256,
|
||||
) -> Result<H256, DispatchError> {
|
||||
let beacon_header_root = beacon_header
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::HeaderHashTreeRootFailed)?;
|
||||
|
||||
let hash_root = SigningData { object_root: beacon_header_root, domain }
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::SigningRootHashTreeRootFailed)?;
|
||||
|
||||
Ok(hash_root)
|
||||
}
|
||||
|
||||
/// Stores a compacted (slot and block roots root (hash of the `block_roots` beacon state
|
||||
/// field, used for ancestry proof)) beacon state in a ring buffer map, with the header root
|
||||
/// as map key.
|
||||
fn store_finalized_header(
|
||||
header_root: H256,
|
||||
header: BeaconHeader,
|
||||
block_roots_root: H256,
|
||||
) -> DispatchResult {
|
||||
let slot = header.slot;
|
||||
|
||||
<FinalizedBeaconStateBuffer<T>>::insert(
|
||||
header_root,
|
||||
CompactBeaconState { slot: header.slot, block_roots_root },
|
||||
);
|
||||
<LatestFinalizedBlockRoot<T>>::set(header_root);
|
||||
|
||||
log::info!(
|
||||
target: LOG_TARGET,
|
||||
"💫 Updated latest finalized block root {} at slot {}.",
|
||||
header_root,
|
||||
slot
|
||||
);
|
||||
|
||||
Self::deposit_event(Event::BeaconHeaderImported { block_hash: header_root, slot });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Stores the provided execution header in pallet storage. The header is stored
|
||||
/// in a ring buffer map, with the block hash as map key. The last imported execution
|
||||
/// header is also kept in storage, for the relayer to check import progress.
|
||||
pub(crate) fn store_execution_header(
|
||||
block_hash: H256,
|
||||
header: CompactExecutionHeader,
|
||||
beacon_slot: u64,
|
||||
beacon_block_root: H256,
|
||||
) {
|
||||
let block_number = header.block_number;
|
||||
|
||||
<ExecutionHeaderBuffer<T>>::insert(block_hash, header);
|
||||
|
||||
log::trace!(
|
||||
target: LOG_TARGET,
|
||||
"💫 Updated latest execution block at {} to number {}.",
|
||||
block_hash,
|
||||
block_number
|
||||
);
|
||||
|
||||
LatestExecutionState::<T>::mutate(|s| {
|
||||
s.beacon_block_root = beacon_block_root;
|
||||
s.beacon_slot = beacon_slot;
|
||||
s.block_hash = block_hash;
|
||||
s.block_number = block_number;
|
||||
});
|
||||
|
||||
Self::deposit_event(Event::ExecutionHeaderImported { block_hash, block_number });
|
||||
}
|
||||
|
||||
/// Stores the validators root in storage. Validators root is the hash tree root of all the
|
||||
/// validators at genesis and is used to used to identify the chain that we are on
|
||||
/// (used in conjunction with the fork version).
|
||||
/// <https://eth2book.info/capella/part3/containers/state/#genesis_validators_root>
|
||||
fn store_validators_root(validators_root: H256) {
|
||||
<ValidatorsRoot<T>>::set(validators_root);
|
||||
}
|
||||
|
||||
/// Returns the domain for the domain_type and fork_version. The domain is used to
|
||||
/// distinguish between the different players in the chain (see DomainTypes
|
||||
/// <https://eth2book.info/capella/part3/config/constants/#domain-types>) and to ensure we are
|
||||
/// addressing the correct chain.
|
||||
/// <https://eth2book.info/capella/part3/helper/misc/#compute_domain>
|
||||
pub(super) fn compute_domain(
|
||||
domain_type: Vec<u8>,
|
||||
fork_version: ForkVersion,
|
||||
genesis_validators_root: H256,
|
||||
) -> Result<H256, DispatchError> {
|
||||
let fork_data_root =
|
||||
Self::compute_fork_data_root(fork_version, genesis_validators_root)?;
|
||||
|
||||
let mut domain = [0u8; 32];
|
||||
domain[0..4].copy_from_slice(&(domain_type));
|
||||
domain[4..32].copy_from_slice(&(fork_data_root.0[..28]));
|
||||
|
||||
Ok(domain.into())
|
||||
}
|
||||
|
||||
/// Computes the fork data root. The fork data root is a merkleization of the current
|
||||
/// fork version and the genesis validators root.
|
||||
fn compute_fork_data_root(
|
||||
current_version: ForkVersion,
|
||||
genesis_validators_root: H256,
|
||||
) -> Result<H256, DispatchError> {
|
||||
let hash_root = ForkData {
|
||||
current_version,
|
||||
genesis_validators_root: genesis_validators_root.into(),
|
||||
}
|
||||
.hash_tree_root()
|
||||
.map_err(|_| Error::<T>::ForkDataHashTreeRootFailed)?;
|
||||
|
||||
Ok(hash_root)
|
||||
}
|
||||
|
||||
/// Checks that the sync committee bits (the votes of the sync committee members,
|
||||
/// represented by bits 0 and 1) is more than a supermajority (2/3 of the votes are
|
||||
/// positive).
|
||||
pub(super) fn sync_committee_participation_is_supermajority(
|
||||
sync_committee_bits: &[u8],
|
||||
) -> DispatchResult {
|
||||
let sync_committee_sum = sync_committee_sum(sync_committee_bits);
|
||||
ensure!(
|
||||
((sync_committee_sum * 3) as usize) >= sync_committee_bits.len() * 2,
|
||||
Error::<T>::SyncCommitteeParticipantsNotSupermajority
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns the fork version based on the current epoch. The hard fork versions
|
||||
/// are defined in pallet config.
|
||||
pub(super) fn compute_fork_version(epoch: u64) -> ForkVersion {
|
||||
Self::select_fork_version(&T::ForkVersions::get(), epoch)
|
||||
}
|
||||
|
||||
/// Returns the fork version based on the current epoch.
|
||||
pub(super) fn select_fork_version(fork_versions: &ForkVersions, epoch: u64) -> ForkVersion {
|
||||
if epoch >= fork_versions.capella.epoch {
|
||||
return fork_versions.capella.version
|
||||
}
|
||||
if epoch >= fork_versions.bellatrix.epoch {
|
||||
return fork_versions.bellatrix.version
|
||||
}
|
||||
if epoch >= fork_versions.altair.epoch {
|
||||
return fork_versions.altair.version
|
||||
}
|
||||
|
||||
fork_versions.genesis.version
|
||||
}
|
||||
|
||||
/// Returns a vector of public keys that participated in the sync committee block signage.
|
||||
/// Sync committee bits is an array of 0s and 1s, 0 meaning the corresponding sync committee
|
||||
/// member did not participate in the vote, 1 meaning they participated.
|
||||
/// This method can find the absent or participating members, based on the participant
|
||||
/// parameter. participant = false will return absent participants, participant = true will
|
||||
/// return participating members.
|
||||
pub fn find_pubkeys(
|
||||
sync_committee_bits: &[u8],
|
||||
sync_committee_pubkeys: &[PublicKeyPrepared],
|
||||
participant: bool,
|
||||
) -> Vec<PublicKeyPrepared> {
|
||||
let mut pubkeys: Vec<PublicKeyPrepared> = Vec::new();
|
||||
for (bit, pubkey) in sync_committee_bits.iter().zip(sync_committee_pubkeys.iter()) {
|
||||
if *bit == u8::from(participant) {
|
||||
pubkeys.push(*pubkey);
|
||||
}
|
||||
}
|
||||
pubkeys
|
||||
}
|
||||
|
||||
/// Calculates signing root for BeaconHeader. The signing root is used for the message
|
||||
/// value in BLS signature verification.
|
||||
pub fn signing_root(
|
||||
header: &BeaconHeader,
|
||||
validators_root: H256,
|
||||
signature_slot: u64,
|
||||
) -> Result<H256, DispatchError> {
|
||||
let fork_version = Self::compute_fork_version(compute_epoch(
|
||||
signature_slot,
|
||||
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.
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,275 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
use crate as ethereum_beacon_client;
|
||||
use frame_support::parameter_types;
|
||||
use pallet_timestamp;
|
||||
use primitives::{Fork, ForkVersions};
|
||||
use sp_core::H256;
|
||||
use sp_runtime::traits::{BlakeTwo256, IdentityLookup};
|
||||
|
||||
#[cfg(not(feature = "beacon-spec-mainnet"))]
|
||||
pub mod minimal {
|
||||
use super::*;
|
||||
|
||||
use crate::config;
|
||||
use hex_literal::hex;
|
||||
use primitives::CompactExecutionHeader;
|
||||
use snowbridge_core::inbound::{Log, Proof};
|
||||
use sp_runtime::BuildStorage;
|
||||
use std::{fs::File, path::PathBuf};
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
pub enum Test {
|
||||
System: frame_system::{Pallet, Call, Storage, Event<T>},
|
||||
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
|
||||
EthereumBeaconClient: ethereum_beacon_client::{Pallet, Call, Storage, Event<T>},
|
||||
}
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub const SS58Prefix: u8 = 42;
|
||||
}
|
||||
|
||||
impl frame_system::Config for Test {
|
||||
type BaseCallFilter = frame_support::traits::Everything;
|
||||
type OnSetCode = ();
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = ();
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type RuntimeTask = RuntimeTask;
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = SS58Prefix;
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
type Nonce = u64;
|
||||
type Block = Block;
|
||||
}
|
||||
|
||||
impl pallet_timestamp::Config for Test {
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = ();
|
||||
type MinimumPeriod = ();
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ExecutionHeadersPruneThreshold: u32 = 10;
|
||||
pub const ChainForkVersions: ForkVersions = ForkVersions{
|
||||
genesis: Fork {
|
||||
version: [0, 0, 0, 1], // 0x00000001
|
||||
epoch: 0,
|
||||
},
|
||||
altair: Fork {
|
||||
version: [1, 0, 0, 1], // 0x01000001
|
||||
epoch: 0,
|
||||
},
|
||||
bellatrix: Fork {
|
||||
version: [2, 0, 0, 1], // 0x02000001
|
||||
epoch: 0,
|
||||
},
|
||||
capella: Fork {
|
||||
version: [3, 0, 0, 1], // 0x03000001
|
||||
epoch: 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
impl ethereum_beacon_client::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type ForkVersions = ChainForkVersions;
|
||||
type MaxExecutionHeadersToKeep = ExecutionHeadersPruneThreshold;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
// Build genesis storage according to the mock runtime.
|
||||
pub fn new_tester() -> sp_io::TestExternalities {
|
||||
let t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
let mut ext = sp_io::TestExternalities::new(t);
|
||||
let _ = ext.execute_with(|| Timestamp::set(RuntimeOrigin::signed(1), 30_000));
|
||||
ext
|
||||
}
|
||||
|
||||
fn load_fixture<T>(basename: &str) -> Result<T, serde_json::Error>
|
||||
where
|
||||
T: for<'de> serde::Deserialize<'de>,
|
||||
{
|
||||
let filepath: PathBuf =
|
||||
[env!("CARGO_MANIFEST_DIR"), "tests", "fixtures", basename].iter().collect();
|
||||
serde_json::from_reader(File::open(filepath).unwrap())
|
||||
}
|
||||
|
||||
pub fn load_execution_header_update_fixture() -> primitives::ExecutionHeaderUpdate {
|
||||
load_fixture("execution-header-update.minimal.json").unwrap()
|
||||
}
|
||||
|
||||
pub fn load_checkpoint_update_fixture(
|
||||
) -> primitives::CheckpointUpdate<{ config::SYNC_COMMITTEE_SIZE }> {
|
||||
load_fixture("initial-checkpoint.minimal.json").unwrap()
|
||||
}
|
||||
|
||||
pub fn load_sync_committee_update_fixture(
|
||||
) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> {
|
||||
load_fixture("sync-committee-update.minimal.json").unwrap()
|
||||
}
|
||||
|
||||
pub fn load_finalized_header_update_fixture(
|
||||
) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> {
|
||||
load_fixture("finalized-header-update.minimal.json").unwrap()
|
||||
}
|
||||
|
||||
pub fn load_next_sync_committee_update_fixture(
|
||||
) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> {
|
||||
load_fixture("next-sync-committee-update.minimal.json").unwrap()
|
||||
}
|
||||
|
||||
pub fn load_next_finalized_header_update_fixture(
|
||||
) -> primitives::Update<{ config::SYNC_COMMITTEE_SIZE }, { config::SYNC_COMMITTEE_BITS_SIZE }> {
|
||||
load_fixture("next-finalized-header-update.minimal.json").unwrap()
|
||||
}
|
||||
|
||||
pub fn get_message_verification_payload() -> (Log, Proof) {
|
||||
(
|
||||
Log {
|
||||
address: hex!("ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0").into(),
|
||||
topics: vec![
|
||||
hex!("1b11dcf133cc240f682dab2d3a8e4cd35c5da8c9cf99adac4336f8512584c5ad").into(),
|
||||
hex!("00000000000000000000000000000000000000000000000000000000000003e8").into(),
|
||||
hex!("0000000000000000000000000000000000000000000000000000000000000001").into(),
|
||||
],
|
||||
data: hex!("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b000f000000000000000100d184c103f7acc340847eee82a0b909e3358bc28d440edffa1352b13227e8ee646f3ea37456dec701345772617070656420457468657210574554481235003511000000000000000000000000000000000000000000").into(),
|
||||
},
|
||||
Proof {
|
||||
block_hash: hex!("05aaa60b0f27cce9e71909508527264b77ee14da7b5bf915fcc4e32715333213").into(),
|
||||
tx_index: 0,
|
||||
data: (vec![
|
||||
hex!("cf0d1c1ba57d1e0edfb59786c7e30c2b7e12bd54612b00cd21c4eaeecedf44fb").to_vec(),
|
||||
hex!("d21fc4f68ab05bc4dcb23c67008e92c4d466437cdd6ed7aad0c008944c185510").to_vec(),
|
||||
hex!("b9890f91ca0d77aa2a4adfaf9b9e40c94cac9e638b6d9797923865872944b646").to_vec(),
|
||||
], vec![
|
||||
hex!("f90131a0b601337b3aa10a671caa724eba641e759399979856141d3aea6b6b4ac59b889ba00c7d5dd48be9060221a02fb8fa213860b4c50d47046c8fa65ffaba5737d569e0a094601b62a1086cd9c9cb71a7ebff9e718f3217fd6e837efe4246733c0a196f63a06a4b0dd0aefc37b3c77828c8f07d1b7a2455ceb5dbfd3c77d7d6aeeddc2f7e8ca0d6e8e23142cdd8ec219e1f5d8b56aa18e456702b195deeaa210327284d42ade4a08a313d4c87023005d1ab631bbfe3f5de1e405d0e66d0bef3e033f1e5711b5521a0bf09a5d9a48b10ade82b8d6a5362a15921c8b5228a3487479b467db97411d82fa0f95cccae2a7c572ef3c566503e30bac2b2feb2d2f26eebf6d870dcf7f8cf59cea0d21fc4f68ab05bc4dcb23c67008e92c4d466437cdd6ed7aad0c008944c1855108080808080808080").to_vec(),
|
||||
hex!("f851a0b9890f91ca0d77aa2a4adfaf9b9e40c94cac9e638b6d9797923865872944b646a060a634b9280e3a23fb63375e7bbdd9ab07fd379ab6a67e2312bbc112195fa358808080808080808080808080808080").to_vec(),
|
||||
hex!("f9030820b9030402f90300018301d6e2b9010000000000000800000000000020040008000000000000000000000000400000008000000000000000000000000000000000000000000000000000000000042010000000001000000000000000000000000000000000040000000000000000000000000000000000000000000000008000000000000000002000000000000000000000000200000000000000200000000000100000000040000001000200008000000000000200000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000f901f5f87a942ffa5ecdbe006d30397c7636d3e015eee251369ff842a0c965575a00553e094ca7c5d14f02e107c258dda06867cbf9e0e69f80e71bbcc1a000000000000000000000000000000000000000000000000000000000000003e8a000000000000000000000000000000000000000000000000000000000000003e8f9011c94ee9170abfbf9421ad6dd07f6bdec9d89f2b581e0f863a01b11dcf133cc240f682dab2d3a8e4cd35c5da8c9cf99adac4336f8512584c5ada000000000000000000000000000000000000000000000000000000000000003e8a00000000000000000000000000000000000000000000000000000000000000001b8a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004b000f000000000000000100d184c103f7acc340847eee82a0b909e3358bc28d440edffa1352b13227e8ee646f3ea37456dec701345772617070656420457468657210574554481235003511000000000000000000000000000000000000000000f858948cf6147918a5cbb672703f879f385036f8793a24e1a01449abf21e49fd025f33495e77f7b1461caefdd3d4bb646424a3f445c4576a5ba0000000000000000000000000440edffa1352b13227e8ee646f3ea37456dec701").to_vec(),
|
||||
]),
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_message_verification_header() -> CompactExecutionHeader {
|
||||
CompactExecutionHeader {
|
||||
parent_hash: hex!("04a7f6ab8282203562c62f38b0ab41d32aaebe2c7ea687702b463148a6429e04")
|
||||
.into(),
|
||||
block_number: 55,
|
||||
state_root: hex!("894d968712976d613519f973a317cb0781c7b039c89f27ea2b7ca193f7befdb3")
|
||||
.into(),
|
||||
receipts_root: hex!("cf0d1c1ba57d1e0edfb59786c7e30c2b7e12bd54612b00cd21c4eaeecedf44fb")
|
||||
.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "beacon-spec-mainnet")]
|
||||
pub mod mainnet {
|
||||
use super::*;
|
||||
|
||||
type Block = frame_system::mocking::MockBlock<Test>;
|
||||
use sp_runtime::BuildStorage;
|
||||
|
||||
frame_support::construct_runtime!(
|
||||
pub enum Test {
|
||||
System: frame_system::{Pallet, Call, Storage, Event<T>},
|
||||
Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent},
|
||||
EthereumBeaconClient: ethereum_beacon_client::{Pallet, Call, Storage, Event<T>},
|
||||
}
|
||||
);
|
||||
|
||||
parameter_types! {
|
||||
pub const BlockHashCount: u64 = 250;
|
||||
pub const SS58Prefix: u8 = 42;
|
||||
}
|
||||
|
||||
impl frame_system::Config for Test {
|
||||
type BaseCallFilter = frame_support::traits::Everything;
|
||||
type OnSetCode = ();
|
||||
type BlockWeights = ();
|
||||
type BlockLength = ();
|
||||
type DbWeight = ();
|
||||
type RuntimeOrigin = RuntimeOrigin;
|
||||
type RuntimeCall = RuntimeCall;
|
||||
type RuntimeTask = RuntimeTask;
|
||||
type Hash = H256;
|
||||
type Hashing = BlakeTwo256;
|
||||
type AccountId = u64;
|
||||
type Lookup = IdentityLookup<Self::AccountId>;
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type BlockHashCount = BlockHashCount;
|
||||
type Version = ();
|
||||
type PalletInfo = PalletInfo;
|
||||
type AccountData = ();
|
||||
type OnNewAccount = ();
|
||||
type OnKilledAccount = ();
|
||||
type SystemWeightInfo = ();
|
||||
type SS58Prefix = SS58Prefix;
|
||||
type MaxConsumers = frame_support::traits::ConstU32<16>;
|
||||
type Nonce = u64;
|
||||
type Block = Block;
|
||||
}
|
||||
|
||||
impl pallet_timestamp::Config for Test {
|
||||
type Moment = u64;
|
||||
type OnTimestampSet = ();
|
||||
type MinimumPeriod = ();
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const ChainForkVersions: ForkVersions = ForkVersions{
|
||||
genesis: Fork {
|
||||
version: [0, 0, 16, 32], // 0x00001020
|
||||
epoch: 0,
|
||||
},
|
||||
altair: Fork {
|
||||
version: [1, 0, 16, 32], // 0x01001020
|
||||
epoch: 36660,
|
||||
},
|
||||
bellatrix: Fork {
|
||||
version: [2, 0, 16, 32], // 0x02001020
|
||||
epoch: 112260,
|
||||
},
|
||||
capella: Fork {
|
||||
version: [3, 0, 16, 32], // 0x03001020
|
||||
epoch: 162304,
|
||||
},
|
||||
};
|
||||
pub const ExecutionHeadersPruneThreshold: u32 = 10;
|
||||
}
|
||||
|
||||
impl ethereum_beacon_client::Config for Test {
|
||||
type RuntimeEvent = RuntimeEvent;
|
||||
type ForkVersions = ChainForkVersions;
|
||||
type MaxExecutionHeadersToKeep = ExecutionHeadersPruneThreshold;
|
||||
type WeightInfo = ();
|
||||
}
|
||||
|
||||
// Build genesis storage according to the mock runtime.
|
||||
pub fn new_tester() -> sp_io::TestExternalities {
|
||||
let t = frame_system::GenesisConfig::<Test>::default().build_storage().unwrap();
|
||||
let mut ext = sp_io::TestExternalities::new(t);
|
||||
let _ = ext.execute_with(|| Timestamp::set(RuntimeOrigin::signed(1), 30_000));
|
||||
ext
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,38 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-FileCopyrightText: 2023 Snowfork <hello@snowfork.com>
|
||||
pub use crate::config::{
|
||||
SLOTS_PER_HISTORICAL_ROOT, SYNC_COMMITTEE_BITS_SIZE as SC_BITS_SIZE,
|
||||
SYNC_COMMITTEE_SIZE as SC_SIZE,
|
||||
};
|
||||
use frame_support::storage::types::OptionQuery;
|
||||
use snowbridge_core::RingBufferMapImpl;
|
||||
|
||||
// Specialize types based on configured sync committee size
|
||||
pub type SyncCommittee = primitives::SyncCommittee<SC_SIZE>;
|
||||
pub type SyncCommitteePrepared = primitives::SyncCommitteePrepared<SC_SIZE>;
|
||||
pub type SyncAggregate = primitives::SyncAggregate<SC_SIZE, SC_BITS_SIZE>;
|
||||
pub type CheckpointUpdate = primitives::CheckpointUpdate<SC_SIZE>;
|
||||
pub type Update = primitives::Update<SC_SIZE, SC_BITS_SIZE>;
|
||||
pub type NextSyncCommitteeUpdate = primitives::NextSyncCommitteeUpdate<SC_SIZE>;
|
||||
|
||||
pub use primitives::ExecutionHeaderUpdate;
|
||||
|
||||
/// ExecutionHeader ring buffer implementation
|
||||
pub type ExecutionHeaderBuffer<T> = RingBufferMapImpl<
|
||||
u32,
|
||||
<T as crate::Config>::MaxExecutionHeadersToKeep,
|
||||
crate::ExecutionHeaderIndex<T>,
|
||||
crate::ExecutionHeaderMapping<T>,
|
||||
crate::ExecutionHeaders<T>,
|
||||
OptionQuery,
|
||||
>;
|
||||
|
||||
/// FinalizedState ring buffer implementation
|
||||
pub(crate) type FinalizedBeaconStateBuffer<T> = RingBufferMapImpl<
|
||||
u32,
|
||||
crate::MaxFinalizedHeadersToKeep<T>,
|
||||
crate::FinalizedBeaconStateIndex<T>,
|
||||
crate::FinalizedBeaconStateMapping<T>,
|
||||
crate::FinalizedBeaconState<T>,
|
||||
OptionQuery,
|
||||
>;
|
||||
@@ -0,0 +1,68 @@
|
||||
//! Autogenerated weights for ethereum_beacon_client
|
||||
//!
|
||||
//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev
|
||||
//! DATE: 2022-09-27, STEPS: `10`, REPEAT: 10, LOW RANGE: `[]`, HIGH RANGE: `[]`
|
||||
//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("/tmp/snowbridge/spec.json"), DB CACHE: 1024
|
||||
|
||||
// Executed Command:
|
||||
// ./target/release/snowbridge
|
||||
// benchmark
|
||||
// pallet
|
||||
// --chain
|
||||
// /tmp/snowbridge/spec.json
|
||||
// --execution=wasm
|
||||
// --pallet
|
||||
// ethereum_beacon_client
|
||||
// --extrinsic
|
||||
// *
|
||||
// --steps
|
||||
// 10
|
||||
// --repeat
|
||||
// 10
|
||||
// --output
|
||||
// pallets/ethereum-beacon-client/src/weights.rs
|
||||
// --template
|
||||
// templates/module-weight-template.hbs
|
||||
|
||||
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||
#![allow(unused_parens)]
|
||||
#![allow(unused_imports)]
|
||||
|
||||
use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
|
||||
use sp_std::marker::PhantomData;
|
||||
|
||||
/// Weight functions needed for ethereum_beacon_client.
|
||||
pub trait WeightInfo {
|
||||
fn force_checkpoint() -> Weight;
|
||||
fn submit() -> Weight;
|
||||
fn submit_with_sync_committee() -> Weight;
|
||||
fn submit_execution_header() -> Weight;
|
||||
}
|
||||
|
||||
// For backwards compatibility and tests
|
||||
impl WeightInfo for () {
|
||||
fn force_checkpoint() -> Weight {
|
||||
Weight::from_parts(97_263_571_000_u64, 0)
|
||||
.saturating_add(Weight::from_parts(0, 3501))
|
||||
.saturating_add(RocksDbWeight::get().reads(2))
|
||||
.saturating_add(RocksDbWeight::get().writes(9))
|
||||
}
|
||||
fn submit() -> Weight {
|
||||
Weight::from_parts(26_051_019_000_u64, 0)
|
||||
.saturating_add(Weight::from_parts(0, 93857))
|
||||
.saturating_add(RocksDbWeight::get().reads(8))
|
||||
.saturating_add(RocksDbWeight::get().writes(4))
|
||||
}
|
||||
fn submit_with_sync_committee() -> Weight {
|
||||
Weight::from_parts(122_461_312_000_u64, 0)
|
||||
.saturating_add(Weight::from_parts(0, 93857))
|
||||
.saturating_add(RocksDbWeight::get().reads(6))
|
||||
.saturating_add(RocksDbWeight::get().writes(1))
|
||||
}
|
||||
fn submit_execution_header() -> Weight {
|
||||
Weight::from_parts(113_158_000_u64, 0)
|
||||
.saturating_add(Weight::from_parts(0, 3537))
|
||||
.saturating_add(RocksDbWeight::get().reads(5))
|
||||
.saturating_add(RocksDbWeight::get().writes(4))
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"header": {
|
||||
"slot": 3622,
|
||||
"proposer_index": 7,
|
||||
"parent_root": "0x254c9215f6cce83e21b9776afb482181639602d3cb58cf99452a6a4a4f603930",
|
||||
"state_root": "0xea98df6d30817d63f3e54ea118e2b1ba8675753c72dec1661c503d4eb43f9bdd",
|
||||
"body_root": "0x765a0616a31d38e0ca2d10f6e8b234dd3d07e16aa929bcbc4de775c93f1972fd"
|
||||
},
|
||||
"ancestry_proof": {
|
||||
"header_branch": [
|
||||
"0x7690506882ac8c5f01d00f3ade06439259a3a0261ef5d61ec44920678b4104e6",
|
||||
"0xf01aa0fdd7c9ef7b1affb7854fe8cbcc5c70643ee5b83e032faa702a0675a8cb",
|
||||
"0x273a7b300b75ffa2c765af50680aa836299264f2107f38010278822313181801",
|
||||
"0x30fe73a3bae6a31af32656ab759a4b67d27a213e01012b96cc4fedd0f2e77c75",
|
||||
"0x7246cb3a35f13a1f0bbf907887985bb5382c45f2aa1699dbca48a0a82d5330af",
|
||||
"0x5e7270e88a22dd4a905b2e76da2c8c358baeddd34de6c64a71bb1c80070ab717"
|
||||
],
|
||||
"finalized_block_root": "0xa6fdc5df11c1759d11c9f0353a666715e5677e9ffd7d414e44cff0970553f1c9"
|
||||
},
|
||||
"execution_header": {
|
||||
"parent_hash": "0x6c9657f1267ad6040ea017ff6d02b55c4ba25cb092b8326d321dd98d01d1ee64",
|
||||
"fee_recipient": "0x0000000000000000000000000000000000000000",
|
||||
"state_root": "0x01f975f7cdff9b0a8844304aa59062fe18af0fef4636539312dfe20d238600ba",
|
||||
"receipts_root": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"logs_bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"prev_randao": "0xcdfcab74bc26b3f4311afdc72d2d21d33a4b045187a01fa208a9d687a6d1d25c",
|
||||
"block_number": 3622,
|
||||
"gas_limit": 30000000,
|
||||
"gas_used": 0,
|
||||
"timestamp": 1685722543,
|
||||
"extra_data": "0xd983010b02846765746888676f312e31392e358664617277696e",
|
||||
"base_fee_per_gas": 7,
|
||||
"block_hash": "0x38c80e0e26cb80730df627d32f50266bd0fe32fb12b7606300ad81aa2b4033db",
|
||||
"transactions_root": "0x7ffe241ea60187fdb0187bfa22de35d1f9bed7ab061d9401fd47e34a54fbede1",
|
||||
"withdrawals_root": "0x28ba1834a3a7b657460ce79fa3a1d909ab8828fd557659d4d0554a9bdbc0ec30"
|
||||
},
|
||||
"execution_branch": [
|
||||
"0x005b8d55b34b4323bfd4773c28b09eb53bc87959e65411ccd23728c7e42d5ff2",
|
||||
"0x336488033fe5f3ef4ccc12af07b9370b92e553e35ecb4a337a1b1c0e4afe1e0e",
|
||||
"0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71",
|
||||
"0x7061330dada1ba1c602ba98f647a441885460ed0db00483fea1282385dfab84b"
|
||||
]
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"attested_header": {
|
||||
"slot": 3640,
|
||||
"proposer_index": 5,
|
||||
"parent_root": "0xf062fcec9c3379a08e6add37a834b1e39af395fc343973e44957ecebbf2ecddd",
|
||||
"state_root": "0xb1581cb62fe376e305e02f26463153f5dfb804d8df97ef40fc315c1bc30731ba",
|
||||
"body_root": "0x98461abcc6d130b7bcb9430292c8a269ea9f01082685347e2968d892f716067c"
|
||||
},
|
||||
"sync_aggregate": {
|
||||
"sync_committee_bits": "0xffffffff",
|
||||
"sync_committee_signature": "0x925c6e4b67890a7e28a7ca19853f88247e92014b9d233ac9058efd4f3827f0055db308debe17596e635b93727b5a851e1366ca801f30b03fdec722f45011504702a27646488b5ab5e3428fe7b4d4a50132f374612f66e45d68db27c568f96f08"
|
||||
},
|
||||
"signature_slot": 3641,
|
||||
"next_sync_committee_update": null,
|
||||
"finalized_header": {
|
||||
"slot": 3624,
|
||||
"proposer_index": 7,
|
||||
"parent_root": "0x7690506882ac8c5f01d00f3ade06439259a3a0261ef5d61ec44920678b4104e6",
|
||||
"state_root": "0x3726ebb8d9973977a71a8389caf5fc5830eeb8cd4fdfbbc7b0c4e6ca3e6a4090",
|
||||
"body_root": "0x0f9a3f0fa5a4ffaf7c10504c86f23e7d554366ffd069fe958a160b253c3fd409"
|
||||
},
|
||||
"finality_branch": [
|
||||
"0xc501000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7",
|
||||
"0x83c3d5360d254f4a44be712c1f433e88e810b6d1e0e789e90bada9e36126b857",
|
||||
"0x97245fa01a89a6d7b4542cd731fef699f58b2bbaabdd6f641334c9e9eeae3a20",
|
||||
"0xc3d19c773f66ab94bc2106d5e75a3205398dd6e94b6f8a5716f347741eb9fc5a",
|
||||
"0x9e5040e56d765c1add56779a716be7497be27cba37f866cd8d34418d55e48715"
|
||||
],
|
||||
"block_roots_root": "0x29a54625749fa25f9e36df14a3baa335c58246bba2f8c7eb8b1ec2e4908e2fd0",
|
||||
"block_roots_branch": [
|
||||
"0x53616f9298818a8423c98adc47c92aaf82f0c5c911dc4ee5f88ba6d3022341c1",
|
||||
"0x5d2f1c4bce6f63f26cbe3fbf480281c04a6b14bea74350a88ee945354ecbd79d",
|
||||
"0x8333eefc7eaa4d10091e2014b3aae2bf6bd2d10c22c67100e189f8ab6caab261",
|
||||
"0x3edfa69130bc193dec47c27a5903f03d5262b75899b69c0e95ac1816a664a3e7",
|
||||
"0x5e046000f85aede8d4c28140b27778488d4ad21b1e16e345055d07ee53f2711b"
|
||||
]
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"header": {
|
||||
"slot": 3616,
|
||||
"proposer_index": 7,
|
||||
"parent_root": "0x6c5e8c7b32b7bfbb250fa8fd7bc348d7325fb2bfc869e4c506af6802fcad87f4",
|
||||
"state_root": "0x3e467e3429a1ae36572fe3fe1c953381242e950254cf97c7527a8cea8aa6c9de",
|
||||
"body_root": "0x7da749680d2b0b4f779047fcfe7d0c13d247f6d23478817fe9c6fbe07993adb2"
|
||||
},
|
||||
"current_sync_committee": {
|
||||
"pubkeys": [
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c"
|
||||
],
|
||||
"aggregate_pubkey": "0x8fe11476a05750c52618deb79918e2e674f56dfbf12dbce55ae4386d108e8a1e83c6326f5957e2ef19137582ce270dc6"
|
||||
},
|
||||
"current_sync_committee_branch": [
|
||||
"0x46af3f54acbea439b63aa5bb699c8f25ff584b23912366788f7c8e95011ce324",
|
||||
"0x41dcb71ec3b3940399118d28e09fdc58a8e33b818b8c5cbb933c59929504ca08",
|
||||
"0xfa53febb29348e3493a50c0e7c6d35796bf69c54dfc6f42f7600612789d0ed6d",
|
||||
"0x5e7ea1693066b604fc60d4657b43e7a4aafd3f4f54d9a740d2abe765e92d8385",
|
||||
"0x16c9bca64a82e80c23817bfec345d088e0adc3865e392965c1244f97979f816a"
|
||||
],
|
||||
"validators_root": "0x270d43e74ce340de4bca2b1936beca0f4f5408d9e78aec4850920baf659d5b69",
|
||||
"block_roots_root": "0x00f6bbdeac1e1a922a9bf0e78720c0bffe558d8195e8ede8cb72bbd295f242f2",
|
||||
"block_roots_branch": [
|
||||
"0x7a61086fb9e53ab4dd87243d6288c51793696168a73773277630da5b20bf6091",
|
||||
"0x60733905cdc5dd65d05161bb3138eecc47d6d6057ab36b0d36cf5a3200484143",
|
||||
"0x86d7de634ae45de5b3cbbc562dd976de7d06a3d96f83147413536e6b108c7a39",
|
||||
"0x0ada571c9e0da6fce8dd13e6d9ce173768521ac32e0af456634556176789fa6e",
|
||||
"0x2341538fd0aafbc1ff0f513545e5dcd4b8905dc9e00d6173480c18a4e8086ebc"
|
||||
]
|
||||
}
|
||||
Vendored
Executable
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"attested_header": {
|
||||
"slot": 3696,
|
||||
"proposer_index": 1,
|
||||
"parent_root": "0x04a63c5dfb726c31a32a72c1c426ff89e21363223d7096486b629f1d58abe5d8",
|
||||
"state_root": "0xbe20e69420cbf9400224ec5edeb0843776a2ccf945e9a3ba9311ae812cad1e30",
|
||||
"body_root": "0x1d2acd1748f1c58096d1edc8badd3a1d7e1dc3c33bcb9229e4c03f3a84efeadb"
|
||||
},
|
||||
"sync_aggregate": {
|
||||
"sync_committee_bits": "0xffffffff",
|
||||
"sync_committee_signature": "0xafa79bc0f3c731ab1eb6aeafc582a7dd1c100ea471df3af6ff485b58661b3ef8077264dea0b60df9aec2d3ca8ddab6770fc9d061462e5a6dc718146085425f863d00921c42413805cb5b4c5175f36f2087cfed740bb7d57e8d5b48352643cd5b"
|
||||
},
|
||||
"signature_slot": 3697,
|
||||
"next_sync_committee_update": null,
|
||||
"finalized_header": {
|
||||
"slot": 3680,
|
||||
"proposer_index": 7,
|
||||
"parent_root": "0x4d8f4fc47ad3eb045bd20cae13af6df02f96a3f8d7c8a285190ba10cfe2b84cf",
|
||||
"state_root": "0xd498766d77277fe16a6a4609ab3ac3a6e9887d162d8dfffdfc9cc4ae833e4127",
|
||||
"body_root": "0x9ba73bc9a4907cac0b887550e2b01a63dcc70473753ffcc243d33394cc64b4c0"
|
||||
},
|
||||
"finality_branch": [
|
||||
"0xcc01000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7",
|
||||
"0x142061c4bc3673bf774cb8c7b6085057bd0ca85672b43afa2d9581b0b6a44e54",
|
||||
"0x48b8cd8ca9d9563e30c1cca2a854cd7f75eb4cb013d10809b3138a72d94ea0c5",
|
||||
"0x9b39523d05013ac7cbb9f43e5d6f9dc033b12aa1d6d6edd994ddc4f5efe7be9d",
|
||||
"0x066c9aa26107bc8cb28bc73e518da6cc865ec1d67516b6ca24663b6b7ae3cb21"
|
||||
],
|
||||
"block_roots_root": "0xb15aa2483811d8c5616cb93710f4fcb809d97443caac9de163f943a30f385db6",
|
||||
"block_roots_branch": [
|
||||
"0xf7a43ad317417daa4c2a1e93c54895895a824ef1e43320eb44eab16673da5a61",
|
||||
"0xe4b8d640660f765c2ef4dc886025dc8e54c6e70b66192582f42837ed5e9d8d41",
|
||||
"0x841f113dc81e76419b6cdec8b0cf2fc20f9381492ed3c79e9b49179b4d3eacbc",
|
||||
"0xeb5fdc4d8b5282b653ecbc9caa93bcfe482f6d6a32cbb0d9eb011bef947579bb",
|
||||
"0x1f328cc5640efb191ae6aa86223b1aa9d083b26ac3e1fa3c071327bb09dc5727"
|
||||
]
|
||||
}
|
||||
Vendored
Executable
+83
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"attested_header": {
|
||||
"slot": 3664,
|
||||
"proposer_index": 4,
|
||||
"parent_root": "0x15ac23a0c16bfa81e8595621118040c3e6cbddd4b09bae6fb39ba5fefd0258e8",
|
||||
"state_root": "0x6fb81aa3827e7d580bb05b4df2686c9a49508bde2f8342fd75be609a23dd8362",
|
||||
"body_root": "0x9906a1ae8065d268f8acb7f1b3119408d2f7f8e6e0764370c16ea3d15134981f"
|
||||
},
|
||||
"sync_aggregate": {
|
||||
"sync_committee_bits": "0xffffffff",
|
||||
"sync_committee_signature": "0xa9b5584ec9290a4ac6c5616639d031f9ab1064d63b4889f1da52f6f4d66b645fca48bbe2fe8484adb0c05c647edd694d0340cf684b8ccf8e34c6d8cf447cfcfdcb856f5abdcfd85ada5a4a04d4c8f6f40c6e99308893c3941485a436d6c8e5f7"
|
||||
},
|
||||
"signature_slot": 3665,
|
||||
"next_sync_committee_update": {
|
||||
"next_sync_committee": {
|
||||
"pubkeys": [
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373"
|
||||
],
|
||||
"aggregate_pubkey": "0x8fe11476a05750c52618deb79918e2e674f56dfbf12dbce55ae4386d108e8a1e83c6326f5957e2ef19137582ce270dc6"
|
||||
},
|
||||
"next_sync_committee_branch": [
|
||||
"0x46af3f54acbea439b63aa5bb699c8f25ff584b23912366788f7c8e95011ce324",
|
||||
"0x5b118fe110ee4a1b0cf9823bc189fb38eb55a7b49adbdafcf466ec7cd4b7fd68",
|
||||
"0xc2f12fb91a61abedb47f62a98258960edca21f31494cdf59b47a1c721e3e98f8",
|
||||
"0x16fdfd5e6b591b3140a76efa4593a9c4d105b9e5c62d6f44edbd24790657be50",
|
||||
"0xc8175ab66690cc94c0a24452754addd62a06948de5db9814e813437a130de452"
|
||||
]
|
||||
},
|
||||
"finalized_header": {
|
||||
"slot": 3648,
|
||||
"proposer_index": 1,
|
||||
"parent_root": "0x991ee98a70e8f90bdd61d0f5554e53d37473e75e16af171f6d88f27d20223dae",
|
||||
"state_root": "0x59b04d660ac772005a13a7dc1d5f99bb0d0292f3c422f04f7365198d70dd30de",
|
||||
"body_root": "0x5151f035e146258e7327ad9cf1df13f8ddec7a7842c19993cf739358717b5565"
|
||||
},
|
||||
"finality_branch": [
|
||||
"0xc801000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7",
|
||||
"0x142061c4bc3673bf774cb8c7b6085057bd0ca85672b43afa2d9581b0b6a44e54",
|
||||
"0xc2f12fb91a61abedb47f62a98258960edca21f31494cdf59b47a1c721e3e98f8",
|
||||
"0x16fdfd5e6b591b3140a76efa4593a9c4d105b9e5c62d6f44edbd24790657be50",
|
||||
"0xc8175ab66690cc94c0a24452754addd62a06948de5db9814e813437a130de452"
|
||||
],
|
||||
"block_roots_root": "0xe6e2adaaad45363d7112945ef670e21c66bcb3276dc450962ade1e8950230380",
|
||||
"block_roots_branch": [
|
||||
"0x386ede102258966d4c23031c5a02de2af8180d475c4c1716b07fb5b9f142a817",
|
||||
"0x35e6c89bc38d993a1957f8a9fb1fbeab7420688091ba2cd7ee7b19b7e187f7d6",
|
||||
"0x99249309825cafef7e694c09c4fdf95eb4b1e8743d3b23f6959d9980ad2d69b0",
|
||||
"0x5e028d1d905db6430f0ce4aafbc78f442047ec3a132b4e69557fdf804a4cfbf3",
|
||||
"0xd34afeab37851937920243683a1c926c41c626aacb145718fce755782d4996dd"
|
||||
]
|
||||
}
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
{
|
||||
"attested_header": {
|
||||
"slot": 3600,
|
||||
"proposer_index": 7,
|
||||
"parent_root": "0xdf60c2d58beccd89678b9267c689e9ba1cf1d58ce5114ad5c16e8341459cfd75",
|
||||
"state_root": "0x023f14c7a38ef4d6ec19b522edfb427c6b70c6ffbd8610ca802dd1491c92c852",
|
||||
"body_root": "0x0f78a1c45e42711efc5fb7b7f6238be1bee9273f7c44ff6892d815858bb77e25"
|
||||
},
|
||||
"sync_aggregate": {
|
||||
"sync_committee_bits": "0xffffffff",
|
||||
"sync_committee_signature": "0xa4dd8f0991de88ca6f81476f72f48cdb67b9414ad7bf6bba37f627c5ec84dd2c2ebc12cddd5d2e7c927276cee2d3d144158b4c067db3e9911fe52fe1875b14c93f90e4eb57bf5e8f0e6e6effe22f9ba076f30207e0ec683354961ae8e9779556"
|
||||
},
|
||||
"signature_slot": 3601,
|
||||
"next_sync_committee_update": {
|
||||
"next_sync_committee": {
|
||||
"pubkeys": [
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c",
|
||||
"0xab0bdda0f85f842f431beaccf1250bf1fd7ba51b4100fd64364b6401fda85bb0069b3e715b58819684e7fc0b10a72a34",
|
||||
"0xa8d4c7c27795a725961317ef5953a7032ed6d83739db8b0e8a72353d1b8b4439427f7efa2c89caa03cc9f28f8cbab8ac",
|
||||
"0xa3a32b0f8b4ddb83f1a0a853d81dd725dfe577d4f4c3db8ece52ce2b026eca84815c1a7e8e92a4de3d755733bf7e4a9b",
|
||||
"0xb89bebc699769726a318c8e9971bd3171297c61aea4a6578a7a4f94b547dcba5bac16a89108b6b6a1fe3695d1a874a0b",
|
||||
"0x88c141df77cd9d8d7a71a75c826c41a9c9f03c6ee1b180f3e7852f6a280099ded351b58d66e653af8e42816a4d8f532e",
|
||||
"0x81283b7a20e1ca460ebd9bbd77005d557370cabb1f9a44f530c4c4c66230f675f8df8b4c2818851aa7d77a80ca5a4a5e",
|
||||
"0x9977f1c8b731a8d5558146bfb86caea26434f3c5878b589bf280a42c9159e700e9df0e4086296c20b011d2e78c27d373",
|
||||
"0xa99a76ed7796f7be22d5b7e85deeb7c5677e88e511e0b337618f8c4eb61349b4bf2d153f649f7b53359fe8b94a38e44c"
|
||||
],
|
||||
"aggregate_pubkey": "0x8fe11476a05750c52618deb79918e2e674f56dfbf12dbce55ae4386d108e8a1e83c6326f5957e2ef19137582ce270dc6"
|
||||
},
|
||||
"next_sync_committee_branch": [
|
||||
"0x1446606d0129c324a4ea374bd29a625175e0659512cd8650097e0a9c38ce6379",
|
||||
"0xd92466c7e9a53b7b55f4fdb151746a3058931d7559b7e84e7b15384ddc903ca0",
|
||||
"0x9fd10c3f68b75cfd3ebd2af0d4e2cbbfbe120e0b5423dde89ff0f743c7a4f937",
|
||||
"0x1ed6aac0ab29a883de2bb2e3579ad4d6807ddcf3db8afcaf0ae25a076ac9a5f4",
|
||||
"0xf17a840df410a15f0e4e48abf521c29ad0d296d3fb4e8b847ea37f2cc8236f1f"
|
||||
]
|
||||
},
|
||||
"finalized_header": {
|
||||
"slot": 3584,
|
||||
"proposer_index": 1,
|
||||
"parent_root": "0x91c285af2ec25d485310391afe667108b787ec570cdbb0e3fd87b1e0e2c47bd7",
|
||||
"state_root": "0xccc4baf90024e035f1252520d2f2ef1e50f840ff0ecc8e6e365721e083871a32",
|
||||
"body_root": "0x91df5e0077434aad609aaa7e030005cee77cca83868ffc2724e5befe9a3f6a02"
|
||||
},
|
||||
"finality_branch": [
|
||||
"0xc001000000000000000000000000000000000000000000000000000000000000",
|
||||
"0x10c726fac935bf9657cc7476d3cfa7bedec5983dcfb59e8a7df6d0a619e108d7",
|
||||
"0x83c3d5360d254f4a44be712c1f433e88e810b6d1e0e789e90bada9e36126b857",
|
||||
"0x9fd10c3f68b75cfd3ebd2af0d4e2cbbfbe120e0b5423dde89ff0f743c7a4f937",
|
||||
"0x1ed6aac0ab29a883de2bb2e3579ad4d6807ddcf3db8afcaf0ae25a076ac9a5f4",
|
||||
"0xf17a840df410a15f0e4e48abf521c29ad0d296d3fb4e8b847ea37f2cc8236f1f"
|
||||
],
|
||||
"block_roots_root": "0x9eab8a05c396a29c32f4f8ac9654fc0fb7cd97ec659236392ede48951a794505",
|
||||
"block_roots_branch": [
|
||||
"0x5c175efdbafacdfdab21c93a318b0e8e2291a5a86c40b1fc564f91ad33c106d4",
|
||||
"0x5c1e0b76176ab033858b2835f90d5e25d708b563f77efd7d9938f0faa1c20878",
|
||||
"0x7aea32464adee801e2a05c3af227f24231d3c088e3b7265a5fada9ac850549fe",
|
||||
"0x9d9fca29e23c5d4ae433adf17e7fd9a0e4d1b09b68f5c45e7ca1b13ebe4a9e98",
|
||||
"0x6b35238f188021c859d6b317457ebb6fe4cf362cab35c988010cb1343eabbfc5"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user