├── spec-tests ├── spec-test-version ├── justfile ├── Cargo.toml ├── runners │ ├── random.rs │ ├── finality.rs │ ├── merkle_proof.rs │ ├── utils.rs │ ├── shuffling.rs │ ├── sanity.rs │ ├── mod.rs │ └── light_client.rs ├── README.md ├── test_utils.rs └── test_case.rs ├── rust-toolchain.toml ├── ethereum-consensus ├── src │ ├── types │ │ ├── presets │ │ │ └── mod.rs │ │ └── mod.rs │ ├── state_transition │ │ ├── presets │ │ │ ├── mod.rs │ │ │ ├── mainnet.rs │ │ │ └── minimal.rs │ │ └── mod.rs │ ├── bellatrix │ │ ├── networking.rs │ │ ├── fork_choice.rs │ │ ├── presets │ │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── beacon_state.rs │ │ ├── fork.rs │ │ ├── blinded_beacon_block.rs │ │ ├── epoch_processing.rs │ │ └── beacon_block.rs │ ├── electra │ │ ├── constants.rs │ │ ├── mod.rs │ │ ├── presets │ │ │ ├── mod.rs │ │ │ ├── minimal.rs │ │ │ └── mainnet.rs │ │ ├── execution_engine.rs │ │ └── operations.rs │ ├── ssz │ │ ├── mod.rs │ │ ├── byte_vector.rs │ │ └── byte_list.rs │ ├── deneb │ │ ├── presets │ │ │ └── mod.rs │ │ ├── networking.rs │ │ ├── mod.rs │ │ ├── execution_engine.rs │ │ ├── epoch_processing.rs │ │ ├── beacon_state.rs │ │ └── light_client.rs │ ├── capella │ │ ├── presets │ │ │ └── mod.rs │ │ ├── withdrawal.rs │ │ ├── mod.rs │ │ ├── bls_to_execution_change.rs │ │ ├── helpers.rs │ │ ├── beacon_state.rs │ │ ├── light_client.rs │ │ ├── epoch_processing.rs │ │ └── blinded_beacon_block.rs │ ├── crypto │ │ └── mod.rs │ ├── altair │ │ ├── networking.rs │ │ ├── presets │ │ │ ├── mod.rs │ │ │ ├── minimal.rs │ │ │ └── mainnet.rs │ │ ├── constants.rs │ │ ├── mod.rs │ │ ├── sync.rs │ │ ├── validator.rs │ │ ├── light_client.rs │ │ ├── beacon_state.rs │ │ ├── beacon_block.rs │ │ └── genesis.rs │ ├── phase0 │ │ ├── constants.rs │ │ ├── mod.rs │ │ ├── networking.rs │ │ ├── presets │ │ │ └── mod.rs │ │ ├── validator.rs │ │ ├── slot_processing.rs │ │ ├── beacon_block.rs │ │ ├── beacon_state.rs │ │ ├── spec │ │ │ └── mod.rs │ │ └── state_transition.rs │ ├── lib.rs │ ├── bin │ │ └── ec │ │ │ ├── validator │ │ │ ├── mnemonic.rs │ │ │ └── mod.rs │ │ │ ├── main.rs │ │ │ ├── bls.rs │ │ │ ├── blobs │ │ │ ├── mod.rs │ │ │ ├── framing.rs │ │ │ ├── bundler.rs │ │ │ ├── decode.rs │ │ │ ├── command.rs │ │ │ └── encode.rs │ │ │ └── README.md │ ├── fork.rs │ ├── domains.rs │ ├── builder │ │ └── mod.rs │ ├── execution_engine.rs │ ├── signing.rs │ ├── primitives.rs │ ├── configs │ │ ├── mod.rs │ │ └── holesky.rs │ └── networks.rs ├── examples │ ├── bls.rs │ ├── read_ssz.rs │ ├── state_transition_across_multiple_forks.rs │ ├── sketch.rs │ └── serde.rs └── Cargo.toml ├── beacon-api-client ├── README.md ├── src │ ├── main.rs │ ├── cli │ │ ├── mod.rs │ │ └── config.rs │ ├── serde.rs │ ├── api_error.rs │ └── lib.rs ├── examples │ ├── post.rs │ ├── get_block.rs │ ├── blob_sketch.rs │ ├── sse.rs │ └── api_sketch.rs └── Cargo.toml ├── rustfmt.toml ├── .gitignore ├── spec-gen ├── Cargo.toml ├── src │ └── main.rs └── README.md ├── justfile ├── LICENSE-MIT ├── .github └── workflows │ └── ci.yml ├── Cargo.toml └── README.md /spec-tests/spec-test-version: -------------------------------------------------------------------------------- 1 | v1.4.0 2 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.85.0" 3 | -------------------------------------------------------------------------------- /ethereum-consensus/src/types/presets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mainnet; 2 | pub mod minimal; 3 | -------------------------------------------------------------------------------- /ethereum-consensus/src/state_transition/presets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mainnet; 2 | pub mod minimal; 3 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bellatrix/networking.rs: -------------------------------------------------------------------------------- 1 | pub const GOSSIP_MAX_SIZE_BELLATRIX: usize = 10 * 2usize.pow(20); 2 | pub const MAX_CHUNK_SIZE_BELLATRIX: usize = 10 * 2usize.pow(20); 3 | -------------------------------------------------------------------------------- /ethereum-consensus/src/electra/constants.rs: -------------------------------------------------------------------------------- 1 | use crate::primitives::Gwei; 2 | 3 | pub const UNSET_DEPOSIT_RECEIPTS_START_INDEX: u64 = u64::MAX; 4 | pub const FULL_EXIT_REQUEST_AMOUNT: Gwei = 0; 5 | -------------------------------------------------------------------------------- /beacon-api-client/README.md: -------------------------------------------------------------------------------- 1 | # beacon-api-client 2 | 3 | **Note**: this crate was merged from https://github.com/ralexstokes/beacon-api-client repository at commit `ea585027b922877b51ec5db42b4e676c98f4d834`. 4 | -------------------------------------------------------------------------------- /ethereum-consensus/src/ssz/mod.rs: -------------------------------------------------------------------------------- 1 | mod byte_list; 2 | mod byte_vector; 3 | 4 | pub mod prelude { 5 | pub use super::{byte_list::ByteList, byte_vector::ByteVector}; 6 | pub use ssz_rs::prelude::*; 7 | } 8 | -------------------------------------------------------------------------------- /ethereum-consensus/src/deneb/presets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mainnet; 2 | pub mod minimal; 3 | 4 | pub struct Preset { 5 | pub field_elements_per_blob: usize, 6 | pub max_blob_commitments_per_block: usize, 7 | pub max_blobs_per_block: usize, 8 | } 9 | -------------------------------------------------------------------------------- /ethereum-consensus/src/capella/presets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mainnet; 2 | pub mod minimal; 3 | 4 | pub struct Preset { 5 | pub max_bls_to_execution_changes: usize, 6 | pub max_withdrawals_per_payload: usize, 7 | pub max_validators_per_withdrawals_sweep: usize, 8 | } 9 | -------------------------------------------------------------------------------- /ethereum-consensus/src/deneb/networking.rs: -------------------------------------------------------------------------------- 1 | pub const MAX_REQUEST_BLOCKS_DENEB: usize = 2usize.pow(7); 2 | pub const MAX_REQUEST_BLOB_SIDECARS: usize = 768; 3 | pub const MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: usize = 2usize.pow(12); 4 | pub const BLOB_SIDECAR_SUBNET_COUNT: usize = 6; 5 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | reorder_imports = true 2 | imports_granularity = "Crate" 3 | use_small_heuristics = "Max" 4 | comment_width = 100 5 | wrap_comments = true 6 | binop_separator = "Back" 7 | trailing_comma = "Vertical" 8 | trailing_semicolon = false 9 | use_field_init_shorthand = true 10 | -------------------------------------------------------------------------------- /beacon-api-client/src/main.rs: -------------------------------------------------------------------------------- 1 | use beacon_api_client::{mainnet::Client, run_cli, CliConfig}; 2 | use clap::Parser; 3 | use url::Url; 4 | 5 | #[tokio::main] 6 | async fn main() { 7 | let args = CliConfig::parse(); 8 | let url = Url::parse(&args.endpoint).unwrap(); 9 | let client = Client::new(url); 10 | run_cli(&client, &args).await; 11 | } 12 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bellatrix/fork_choice.rs: -------------------------------------------------------------------------------- 1 | use crate::{primitives::Hash32, ssz::prelude::*}; 2 | 3 | #[derive(Default, Debug, SimpleSerialize, Clone, serde::Serialize, serde::Deserialize)] 4 | pub struct PowBlock { 5 | block_hash: Hash32, 6 | parent_hash: Hash32, 7 | #[serde(with = "crate::serde::as_str")] 8 | total_difficulty: U256, 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | consensus-spec-tests 13 | 14 | .env 15 | -------------------------------------------------------------------------------- /ethereum-consensus/src/electra/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod beacon_block; 2 | pub mod beacon_state; 3 | pub mod block_processing; 4 | pub mod constants; 5 | pub mod epoch_processing; 6 | pub mod execution_engine; 7 | pub mod execution_payload; 8 | pub mod fork; 9 | pub mod genesis; 10 | pub mod helpers; 11 | pub mod operations; 12 | pub mod presets; 13 | pub mod spec; 14 | 15 | pub use spec::*; 16 | 17 | pub use presets::{mainnet, minimal, Preset}; 18 | -------------------------------------------------------------------------------- /ethereum-consensus/src/crypto/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bls; 2 | pub mod kzg; 3 | 4 | pub use bls::{ 5 | aggregate, aggregate_verify, eth_aggregate_public_keys, eth_fast_aggregate_verify, 6 | fast_aggregate_verify, hash, verify_signature, Error as BlsError, PublicKey, SecretKey, 7 | Signature, 8 | }; 9 | pub use kzg::{ 10 | kzg_settings_with_precompute_arc, Error as KzgError, KzgCommitment, KzgProof, KzgSettings, 11 | PRECOMPUTE, 12 | }; 13 | -------------------------------------------------------------------------------- /spec-gen/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spec-gen" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | readme = "./README.md" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | syn = { workspace = true } 12 | prettyplease = { workspace = true } 13 | quote = { workspace = true } 14 | clap = { workspace = true } 15 | convert_case = { workspace = true } 16 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bellatrix/presets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mainnet; 2 | pub mod minimal; 3 | 4 | pub struct Preset { 5 | pub inactivity_penalty_quotient_bellatrix: u64, 6 | pub min_slashing_penalty_quotient_bellatrix: u64, 7 | pub proportional_slashing_multiplier_bellatrix: u64, 8 | pub max_bytes_per_transaction: usize, 9 | pub max_transactions_per_payload: usize, 10 | pub bytes_per_logs_bloom: usize, 11 | pub max_extra_data_bytes: usize, 12 | } 13 | -------------------------------------------------------------------------------- /ethereum-consensus/src/state_transition/mod.rs: -------------------------------------------------------------------------------- 1 | mod context; 2 | mod executor; 3 | mod presets; 4 | 5 | pub use context::*; 6 | pub use executor::*; 7 | 8 | pub type Result = std::result::Result; 9 | 10 | #[derive(Clone, Copy)] 11 | pub enum Validation { 12 | Enabled, 13 | Disabled, 14 | } 15 | 16 | pub mod mainnet { 17 | pub use super::presets::mainnet::*; 18 | } 19 | 20 | pub mod minimal { 21 | pub use super::presets::minimal::*; 22 | } 23 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/networking.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::constants::SYNC_COMMITTEE_SUBNET_COUNT, phase0::networking::ATTESTATION_SUBNET_COUNT, 3 | ssz::prelude::Bitvector, 4 | }; 5 | 6 | #[derive(serde::Serialize, serde::Deserialize)] 7 | pub struct MetaData { 8 | #[serde(with = "crate::serde::as_str")] 9 | pub seq_number: u64, 10 | pub attnets: Bitvector, 11 | pub syncnets: Bitvector, 12 | } 13 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/presets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mainnet; 2 | pub mod minimal; 3 | 4 | use crate::primitives::Epoch; 5 | 6 | pub struct Preset { 7 | pub inactivity_penalty_quotient_altair: u64, 8 | pub min_slashing_penalty_quotient_altair: u64, 9 | pub proportional_slashing_multiplier_altair: u64, 10 | pub sync_committee_size: usize, 11 | pub epochs_per_sync_committee_period: Epoch, 12 | pub min_sync_committee_participants: usize, 13 | pub update_timeout: usize, 14 | } 15 | -------------------------------------------------------------------------------- /beacon-api-client/examples/post.rs: -------------------------------------------------------------------------------- 1 | use beacon_api_client::mainnet::Client; 2 | use ethereum_consensus::builder::SignedValidatorRegistration; 3 | use url::Url; 4 | 5 | #[tokio::main] 6 | async fn main() { 7 | let client = Client::new(Url::parse("http://localhost:8080").unwrap()); 8 | let data = SignedValidatorRegistration::default(); 9 | let response = client.http_post("/eth/v1/builder/validators", &data).await.unwrap(); 10 | dbg!(&response); 11 | dbg!(&response.status()); 12 | dbg!(&response.text().await); 13 | } 14 | -------------------------------------------------------------------------------- /beacon-api-client/src/cli/mod.rs: -------------------------------------------------------------------------------- 1 | mod config; 2 | use crate::mainnet::Client; 3 | pub use config::CliConfig; 4 | use config::{BeaconMethod, Namespace::Beacon}; 5 | 6 | pub async fn run_cli(client: &Client, args: &CliConfig) { 7 | match &args.namespace { 8 | Beacon(BeaconMethod::Genesis) => { 9 | println!("{:?}", &client.get_genesis_details().await.unwrap()); 10 | } 11 | Beacon(BeaconMethod::Root(arg)) => { 12 | println!("{}", &client.get_state_root(arg.state_id.clone()).await.unwrap()) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/constants.rs: -------------------------------------------------------------------------------- 1 | pub const BASE_REWARDS_PER_EPOCH: u64 = 4; 2 | pub const DEPOSIT_CONTRACT_TREE_DEPTH: usize = 2usize.pow(5); 3 | pub const JUSTIFICATION_BITS_LENGTH: usize = 4; 4 | pub const DEPOSIT_DATA_LIST_BOUND: usize = 2usize.pow(DEPOSIT_CONTRACT_TREE_DEPTH as u32); 5 | 6 | pub use crate::phase0::networking::{ 7 | ATTESTATION_PROPAGATION_SLOT_RANGE, ATTESTATION_SUBNET_COUNT, GOSSIP_MAX_SIZE, 8 | MAXIMUM_GOSSIP_CLOCK_DISPARITY, MAX_CHUNK_SIZE, MAX_REQUEST_BLOCKS, 9 | MIN_EPOCHS_FOR_BLOCK_REQUESTS, RESP_TIMEOUT, TTFB_TIMEOUT, 10 | }; 11 | -------------------------------------------------------------------------------- /spec-gen/src/main.rs: -------------------------------------------------------------------------------- 1 | mod generator; 2 | mod type_generator; 3 | mod visitors; 4 | 5 | use clap::{Parser, ValueEnum}; 6 | 7 | #[derive(Parser, Debug)] 8 | struct Args { 9 | command: Command, 10 | } 11 | 12 | #[derive(ValueEnum, Debug, Clone)] 13 | enum Command { 14 | Forks, 15 | Types, 16 | } 17 | 18 | fn main() { 19 | let args = Args::parse(); 20 | match args.command { 21 | Command::Forks => { 22 | generator::run(); 23 | } 24 | Command::Types => { 25 | type_generator::run(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ethereum-consensus/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod altair; 2 | pub mod bellatrix; 3 | pub mod builder; 4 | pub mod capella; 5 | pub mod clock; 6 | pub mod configs; 7 | pub mod crypto; 8 | pub mod deneb; 9 | pub mod domains; 10 | pub mod electra; 11 | pub mod error; 12 | pub mod execution_engine; 13 | mod fork; 14 | pub mod networking; 15 | pub mod networks; 16 | pub mod phase0; 17 | pub mod primitives; 18 | #[cfg(feature = "serde")] 19 | pub mod serde; 20 | pub mod signing; 21 | pub mod ssz; 22 | pub mod state_transition; 23 | pub mod types; 24 | 25 | pub use error::Error; 26 | pub use fork::Fork; 27 | -------------------------------------------------------------------------------- /beacon-api-client/examples/get_block.rs: -------------------------------------------------------------------------------- 1 | use beacon_api_client::{mainnet::Client, BlockId}; 2 | use ethereum_consensus::primitives::Root; 3 | use hex::FromHex; 4 | use url::Url; 5 | 6 | #[tokio::main] 7 | async fn main() { 8 | let url = Url::parse("http://localhost:5052").unwrap(); 9 | let client = Client::new(url); 10 | 11 | let root_hex = "421c16805c3416150aa04533fdfe7fc3f0880d0ed86cee33fa58011f10dd95c8"; 12 | let root = Root::from_hex(root_hex).unwrap(); 13 | let id = BlockId::Root(root); 14 | 15 | let block = client.get_beacon_block(id).await.unwrap(); 16 | dbg!(block); 17 | } 18 | -------------------------------------------------------------------------------- /ethereum-consensus/src/capella/withdrawal.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | primitives::{ExecutionAddress, Gwei, ValidatorIndex, WithdrawalIndex}, 3 | ssz::prelude::*, 4 | }; 5 | 6 | #[derive( 7 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 8 | )] 9 | pub struct Withdrawal { 10 | #[serde(with = "crate::serde::as_str")] 11 | pub index: WithdrawalIndex, 12 | #[serde(with = "crate::serde::as_str")] 13 | pub validator_index: ValidatorIndex, 14 | pub address: ExecutionAddress, 15 | #[serde(with = "crate::serde::as_str")] 16 | pub amount: Gwei, 17 | } 18 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/constants.rs: -------------------------------------------------------------------------------- 1 | pub const TIMELY_SOURCE_FLAG_INDEX: usize = 0; 2 | pub const TIMELY_TARGET_FLAG_INDEX: usize = 1; 3 | pub const TIMELY_HEAD_FLAG_INDEX: usize = 2; 4 | pub const TIMELY_SOURCE_WEIGHT: u64 = 14; 5 | pub const TIMELY_TARGET_WEIGHT: u64 = 26; 6 | pub const TIMELY_HEAD_WEIGHT: u64 = 14; 7 | pub const SYNC_REWARD_WEIGHT: u64 = 2; 8 | pub const PROPOSER_WEIGHT: u64 = 8; 9 | pub const WEIGHT_DENOMINATOR: u64 = 64; 10 | pub const PARTICIPATION_FLAG_WEIGHTS: [u64; 3] = 11 | [TIMELY_SOURCE_WEIGHT, TIMELY_TARGET_WEIGHT, TIMELY_HEAD_WEIGHT]; 12 | 13 | pub const SYNC_COMMITTEE_SUBNET_COUNT: usize = 4; 14 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides an implementation of the `altair` fork 2 | //! of the consensus spec. The primary entrypoints should be one of 3 | //! the "presets" like `mainnet` or `minimal`. 4 | pub mod beacon_block; 5 | pub mod beacon_state; 6 | pub mod block_processing; 7 | pub mod constants; 8 | pub mod epoch_processing; 9 | pub mod fork; 10 | pub mod genesis; 11 | pub mod helpers; 12 | pub mod light_client; 13 | pub mod networking; 14 | pub mod presets; 15 | pub mod spec; 16 | pub mod sync; 17 | pub mod validator; 18 | 19 | pub use spec::*; 20 | 21 | pub use presets::{mainnet, minimal, Preset}; 22 | -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | gen-spec: 2 | cargo run -p spec-gen -- forks 3 | just fmt 4 | 5 | gen-types: 6 | cargo run -p spec-gen -- types 7 | just fmt 8 | 9 | test: 10 | cargo test --all-features --all-targets --workspace --exclude spec-tests 11 | run-spec-tests filter="": 12 | cargo test -p spec-tests {{filter}} 13 | fmt: 14 | cargo +nightly fmt --all 15 | lint: fmt 16 | cargo +nightly clippy --all-targets --all-features --workspace 17 | build: 18 | cargo build --all-targets --all-features --workspace 19 | run-ci: lint build test 20 | ec +command: 21 | cargo run -p ethereum-consensus --features ec {{command}} 22 | -------------------------------------------------------------------------------- /spec-tests/justfile: -------------------------------------------------------------------------------- 1 | spec-test-version := `cat spec-test-version` 2 | 3 | download-integration-tests: clean-integration-tests 4 | #!/usr/bin/env sh 5 | TESTS_TAG={{spec-test-version}} 6 | REPO_NAME=consensus-spec-tests 7 | CONFIGS="general minimal mainnet" 8 | mkdir ${REPO_NAME} 9 | for config in ${CONFIGS} 10 | do 11 | wget https://github.com/ethereum/${REPO_NAME}/releases/download/${TESTS_TAG}/${config}.tar.gz 12 | tar -xzf ${config}.tar.gz -C ${REPO_NAME} 13 | done 14 | rm -f *tar.gz 15 | clean-integration-tests: 16 | rm -rf consensus-spec-tests 17 | test: 18 | cargo test 19 | -------------------------------------------------------------------------------- /ethereum-consensus/examples/bls.rs: -------------------------------------------------------------------------------- 1 | use ethereum_consensus::crypto::{verify_signature, SecretKey}; 2 | use rand::prelude::*; 3 | 4 | /// A simple example that adopts the original BLST's example: 5 | /// https://github.com/supranational/blst/tree/master/bindings/rust 6 | fn main() { 7 | let mut rng = thread_rng(); 8 | let sk = SecretKey::random(&mut rng).expect("can randomly generate a secret key"); 9 | let pk = sk.public_key(); 10 | 11 | let msg = b"blst is such a blast"; 12 | let sig = sk.sign(msg); 13 | 14 | let valid = verify_signature(&pk, msg, &sig).is_ok(); 15 | println!("Signature is valid: {valid:?}"); 16 | } 17 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides an implementation of the `phase0` fork 2 | //! of the consensus spec. The primary entrypoints should be one of 3 | //! the "presets" like `mainnet` or `minimal`. 4 | pub mod beacon_block; 5 | pub mod beacon_state; 6 | pub mod block_processing; 7 | pub mod constants; 8 | pub mod epoch_processing; 9 | pub mod genesis; 10 | pub mod helpers; 11 | pub mod networking; 12 | pub mod operations; 13 | pub mod presets; 14 | pub mod slot_processing; 15 | pub mod spec; 16 | pub mod state_transition; 17 | pub mod validator; 18 | 19 | pub use spec::*; 20 | 21 | pub use presets::{mainnet, minimal, Preset}; 22 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bellatrix/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides an implementation of the `bellatrix` fork 2 | //! of the consensus spec. The primary entrypoints should be one of 3 | //! the "presets" like `mainnet` or `minimal`. 4 | pub mod beacon_block; 5 | pub mod beacon_state; 6 | pub mod blinded_beacon_block; 7 | pub mod block_processing; 8 | pub mod epoch_processing; 9 | pub mod execution_payload; 10 | pub mod fork; 11 | pub mod fork_choice; 12 | pub mod genesis; 13 | pub mod helpers; 14 | pub mod networking; 15 | pub mod presets; 16 | pub mod spec; 17 | pub mod state_transition; 18 | 19 | pub use spec::*; 20 | 21 | pub use presets::{mainnet, minimal, Preset}; 22 | -------------------------------------------------------------------------------- /ethereum-consensus/src/capella/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides an implementation of the `capella` fork 2 | //! of the consensus spec. The primary entrypoints should be one of 3 | //! the "presets" like `mainnet` or `minimal`. 4 | pub mod beacon_block; 5 | pub mod beacon_state; 6 | pub mod blinded_beacon_block; 7 | pub mod block_processing; 8 | pub mod bls_to_execution_change; 9 | pub mod epoch_processing; 10 | pub mod execution_payload; 11 | pub mod fork; 12 | pub mod genesis; 13 | pub mod helpers; 14 | pub mod light_client; 15 | pub mod presets; 16 | pub mod spec; 17 | pub mod withdrawal; 18 | 19 | pub use spec::*; 20 | 21 | pub use presets::{mainnet, minimal, Preset}; 22 | -------------------------------------------------------------------------------- /ethereum-consensus/src/deneb/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module provides an implementation of the `deneb` fork 2 | //! of the consensus spec. The primary entrypoints should be one of 3 | //! the "presets" like `mainnet` or `minimal`. 4 | pub mod beacon_block; 5 | pub mod beacon_state; 6 | pub mod blinded_beacon_block; 7 | pub mod blob_sidecar; 8 | pub mod block_processing; 9 | pub mod epoch_processing; 10 | pub mod execution_engine; 11 | pub mod execution_payload; 12 | pub mod fork; 13 | pub mod genesis; 14 | pub mod helpers; 15 | pub mod light_client; 16 | pub mod networking; 17 | pub mod presets; 18 | pub mod spec; 19 | 20 | pub use spec::*; 21 | 22 | pub use presets::{mainnet, minimal, Preset}; 23 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/validator/mnemonic.rs: -------------------------------------------------------------------------------- 1 | use bip39::{Error, Mnemonic}; 2 | use rand_core::{OsRng, RngCore}; 3 | use std::str::FromStr; 4 | 5 | const KEY_SIZE: usize = 32; 6 | 7 | pub type Seed = [u8; 64]; 8 | 9 | pub fn generate_random_from_system_entropy() -> Result { 10 | let mut entropy = [0u8; KEY_SIZE]; 11 | OsRng.fill_bytes(&mut entropy); 12 | 13 | Mnemonic::from_entropy(&entropy) 14 | } 15 | 16 | pub fn recover_from_phrase(phrase: &str) -> Result { 17 | Mnemonic::from_str(phrase) 18 | } 19 | 20 | pub fn to_seed(mnemonic: Mnemonic, passphrase: Option<&str>) -> Seed { 21 | mnemonic.to_seed(passphrase.unwrap_or("")) 22 | } 23 | -------------------------------------------------------------------------------- /beacon-api-client/src/serde.rs: -------------------------------------------------------------------------------- 1 | pub(crate) use ethereum_consensus::serde::{as_hex, as_str, seq_of_str}; 2 | 3 | pub(crate) mod as_u16 { 4 | use http::StatusCode; 5 | use serde::{de::Deserializer, Deserialize, Serializer}; 6 | 7 | pub fn serialize(x: &StatusCode, s: S) -> Result 8 | where 9 | S: Serializer, 10 | { 11 | s.serialize_u16(x.as_u16()) 12 | } 13 | 14 | pub fn deserialize<'de, D>(deserializer: D) -> Result 15 | where 16 | D: Deserializer<'de>, 17 | { 18 | let value: u16 = Deserialize::deserialize(deserializer)?; 19 | StatusCode::from_u16(value).map_err(serde::de::Error::custom) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /spec-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spec-tests" 3 | version = "0.0.1" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [dependencies] 9 | ssz_rs = { workspace = true } 10 | ethereum-consensus = { path = "../ethereum-consensus", features = [ 11 | "serde", 12 | "secret-key-debug", 13 | "spec-tests", 14 | ] } 15 | libtest-mimic = "0.7.0" 16 | heck = "0.5.0" 17 | serde = { workspace = true } 18 | serde_with = "3.7.0" 19 | snap = "1" 20 | serde_yaml = { workspace = true } 21 | thiserror = { workspace = true } 22 | paste = "1.0.14" 23 | 24 | [[test]] 25 | name = "spec-tests" 26 | path = "main.rs" 27 | harness = false 28 | -------------------------------------------------------------------------------- /beacon-api-client/examples/blob_sketch.rs: -------------------------------------------------------------------------------- 1 | use beacon_api_client::{mainnet::Client, BlockId}; 2 | use url::Url; 3 | 4 | #[tokio::main] 5 | async fn main() { 6 | let username = dotenv::var("NODE_USERNAME").unwrap(); 7 | let password = dotenv::var("NODE_PASSWORD").unwrap(); 8 | let devnet_name = "dencun-devnet-8"; 9 | let cl = "teku"; 10 | let el = "geth"; 11 | 12 | let url_str = 13 | format!("https://{username}:{password}@bn.{cl}-{el}-1.srv.{devnet_name}.ethpandaops.io",); 14 | 15 | let url = Url::parse(&url_str).unwrap(); 16 | let client = Client::new(url); 17 | 18 | let id = BlockId::Slot(194496); 19 | let indices = []; 20 | 21 | let blobs = client.get_blob_sidecars(id, &indices).await.unwrap(); 22 | dbg!(blobs); 23 | } 24 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/main.rs: -------------------------------------------------------------------------------- 1 | mod blobs; 2 | mod bls; 3 | mod validator; 4 | 5 | use clap::{Parser, Subcommand}; 6 | 7 | #[derive(Debug, Subcommand)] 8 | pub enum Commands { 9 | Validator(validator::Command), 10 | Bls(bls::Command), 11 | Blobs(blobs::Command), 12 | } 13 | 14 | #[derive(Debug, Parser)] 15 | #[clap(author, version, about = "utilities for ethereum consensus", long_about = None)] 16 | pub struct Cli { 17 | #[clap(subcommand)] 18 | command: Commands, 19 | } 20 | 21 | fn main() -> eyre::Result<()> { 22 | let cli = Cli::parse(); 23 | 24 | match cli.command { 25 | Commands::Validator(cmd) => cmd.execute(), 26 | Commands::Bls(cmd) => cmd.execute(), 27 | Commands::Blobs(cmd) => cmd.execute(), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/bls.rs: -------------------------------------------------------------------------------- 1 | use clap::Args; 2 | use ethereum_consensus::crypto::SecretKey; 3 | use rand::prelude::*; 4 | use ruint::{aliases::U256, uint}; 5 | 6 | pub(crate) const MODULUS: U256 = 7 | uint!(52435875175126190479447740508185965837690552500527637822603658699938581184513_U256); 8 | 9 | #[derive(Debug, Args)] 10 | #[clap(about = "generate a random BLS12-381 keypair")] 11 | pub struct Command; 12 | 13 | impl Command { 14 | pub fn execute(self) -> eyre::Result<()> { 15 | let mut rng = thread_rng(); 16 | let secret_key = SecretKey::random(&mut rng).unwrap(); 17 | let public_key = secret_key.public_key(); 18 | println!("secret key: {secret_key:?}"); 19 | println!("public key: {public_key:?}"); 20 | Ok(()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /ethereum-consensus/src/electra/presets/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::primitives::Gwei; 2 | 3 | pub mod mainnet; 4 | pub mod minimal; 5 | 6 | pub struct Preset { 7 | pub min_activation_balance: Gwei, 8 | pub max_effective_balance_electra: Gwei, 9 | pub min_slashing_penalty_quotient_electra: u64, 10 | pub whistleblower_reward_quotient_electra: u64, 11 | pub pending_balance_deposits_limit: usize, 12 | pub pending_partial_withdrawals_limit: usize, 13 | pub pending_consolidations_limit: usize, 14 | pub max_attester_slashings_electra: usize, 15 | pub max_attestations_electra: usize, 16 | pub max_consolidations: usize, 17 | pub max_deposit_receipts_per_payload: usize, 18 | pub max_withdrawal_requests_per_payload: usize, 19 | pub max_pending_partials_per_withdrawals_sweep: usize, 20 | } 21 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/sync.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | primitives::{BlsPublicKey, BlsSignature}, 3 | ssz::prelude::*, 4 | }; 5 | 6 | #[derive( 7 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 8 | )] 9 | pub struct SyncAggregate { 10 | pub sync_committee_bits: Bitvector, 11 | pub sync_committee_signature: BlsSignature, 12 | } 13 | 14 | #[derive( 15 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 16 | )] 17 | pub struct SyncCommittee { 18 | #[serde(rename = "pubkeys")] 19 | pub public_keys: Vector, 20 | #[serde(rename = "aggregate_pubkey")] 21 | pub aggregate_public_key: BlsPublicKey, 22 | } 23 | -------------------------------------------------------------------------------- /ethereum-consensus/src/capella/bls_to_execution_change.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | primitives::{BlsPublicKey, BlsSignature, ExecutionAddress, ValidatorIndex}, 3 | ssz::prelude::*, 4 | }; 5 | 6 | #[derive( 7 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 8 | )] 9 | pub struct BlsToExecutionChange { 10 | #[serde(with = "crate::serde::as_str")] 11 | pub validator_index: ValidatorIndex, 12 | #[serde(rename = "from_bls_pubkey")] 13 | pub from_bls_public_key: BlsPublicKey, 14 | pub to_execution_address: ExecutionAddress, 15 | } 16 | 17 | #[derive( 18 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 19 | )] 20 | pub struct SignedBlsToExecutionChange { 21 | pub message: BlsToExecutionChange, 22 | pub signature: BlsSignature, 23 | } 24 | -------------------------------------------------------------------------------- /ethereum-consensus/src/fork.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | // Identifies the fork of the protocol the associated object belongs to. 4 | #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] 5 | #[serde(rename_all = "lowercase")] 6 | pub enum Fork { 7 | Phase0, 8 | Altair, 9 | Bellatrix, 10 | Capella, 11 | Deneb, 12 | Electra, 13 | } 14 | 15 | impl fmt::Display for Fork { 16 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 17 | match self { 18 | Self::Phase0 => write!(f, "phase0"), 19 | Self::Altair => write!(f, "altair"), 20 | Self::Bellatrix => write!(f, "bellatrix"), 21 | Self::Capella => write!(f, "capella"), 22 | Self::Deneb => write!(f, "deneb"), 23 | Self::Electra => write!(f, "electra"), 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/networking.rs: -------------------------------------------------------------------------------- 1 | use crate::{primitives::Epoch, ssz::prelude::Bitvector}; 2 | use std::time::Duration; 3 | 4 | pub const ATTESTATION_SUBNET_COUNT: usize = 64; 5 | pub const GOSSIP_MAX_SIZE: usize = 2usize.pow(20); 6 | pub const MAX_REQUEST_BLOCKS: usize = 2usize.pow(10); 7 | pub const MIN_EPOCHS_FOR_BLOCK_REQUESTS: Epoch = 33024; 8 | pub const MAX_CHUNK_SIZE: usize = 2usize.pow(20); 9 | pub const TTFB_TIMEOUT: Duration = Duration::from_secs(5); 10 | pub const RESP_TIMEOUT: Duration = Duration::from_secs(10); 11 | pub const ATTESTATION_PROPAGATION_SLOT_RANGE: usize = 32; 12 | pub const MAXIMUM_GOSSIP_CLOCK_DISPARITY: Duration = Duration::from_millis(500); 13 | 14 | #[derive(serde::Serialize, serde::Deserialize)] 15 | pub struct MetaData { 16 | #[serde(with = "crate::serde::as_str")] 17 | pub seq_number: u64, 18 | pub attnets: Bitvector, 19 | } 20 | -------------------------------------------------------------------------------- /beacon-api-client/examples/sse.rs: -------------------------------------------------------------------------------- 1 | use beacon_api_client::{mainnet::Client, Error, PayloadAttributesTopic}; 2 | use ethereum_consensus::Fork; 3 | use futures_util::StreamExt; 4 | use tracing::warn; 5 | use url::Url; 6 | 7 | #[tokio::main] 8 | async fn main() -> Result<(), Error> { 9 | tracing_subscriber::fmt::init(); 10 | 11 | let client = Client::new(Url::parse("http://localhost:5052").unwrap()); 12 | let mut events = client.get_events::().await?; 13 | 14 | while let Some(event) = events.next().await { 15 | match event { 16 | Ok(event) => match event.version { 17 | Fork::Capella => { 18 | let payload_attributes = event.data; 19 | dbg!(payload_attributes); 20 | } 21 | _ => todo!(), 22 | }, 23 | Err(err) => warn!(%err), 24 | } 25 | } 26 | Ok(()) 27 | } 28 | -------------------------------------------------------------------------------- /spec-tests/runners/random.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | runners::{ 3 | gen_exec, gen_match_for_all, 4 | utils::{load_blocks_test, run_blocks_test}, 5 | }, 6 | test_case::TestCase, 7 | test_utils::Error, 8 | }; 9 | use ethereum_consensus::state_transition::Validation; 10 | 11 | pub fn dispatch(test: &TestCase) -> Result<(), Error> { 12 | match test.meta.handler.0.as_str() { 13 | "random" => { 14 | gen_match_for_all! { 15 | test, 16 | load_blocks_test, 17 | |(pre, post, blocks): (spec::BeaconState, Option, Vec), context| { 18 | run_blocks_test(pre, post, blocks, context, |state, signed_block, context| { spec::state_transition(state, signed_block, Validation::Enabled, context) }) 19 | } 20 | } 21 | } 22 | handler => unreachable!("no tests for {handler}"), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /spec-tests/README.md: -------------------------------------------------------------------------------- 1 | # Tests from ethereum consensus spec tests 2 | 3 | Refer to the [consensus spec tests repo](https://github.com/ethereum/consensus-spec-tests) for more information. 4 | 5 | ## Updating spec test version 6 | 7 | The current supported spec test version corresponds to releases in the `consensus-spec-tests` repo. 8 | 9 | To update to a new version, update the text file [spec-test-version](./spec-test-version). 10 | 11 | For example, to update to [spec tests release v1.4.0](https://github.com/ethereum/consensus-spec-tests/releases/tag/v1.4.0), the file should simply read: 12 | 13 | ```bash 14 | v1.4.0 15 | ``` 16 | 17 | ## Running tests 18 | 19 | In order to run the tests against the consensus spec test repo, use the following commands: 20 | 21 | ``` 22 | just download-integration-tests 23 | just test 24 | ``` 25 | 26 | ## Notes 27 | 28 | The tests for `ssz_generic` handler are present in the [`ssz-rs` repo](https://github.com/ralexstokes/ssz-rs). 29 | -------------------------------------------------------------------------------- /spec-tests/runners/finality.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | runners::{ 3 | gen_exec, gen_match_for_all, 4 | utils::{load_blocks_test, run_blocks_test}, 5 | }, 6 | test_case::TestCase, 7 | test_utils::Error, 8 | }; 9 | use ethereum_consensus::state_transition::Validation; 10 | 11 | pub fn dispatch(test: &TestCase) -> Result<(), Error> { 12 | match test.meta.handler.0.as_str() { 13 | "finality" => { 14 | gen_match_for_all! { 15 | test, 16 | load_blocks_test, 17 | |(pre, post, blocks): (spec::BeaconState, Option, Vec), context| { 18 | run_blocks_test(pre, post, blocks, context, |state, signed_block, context| { spec::state_transition(state, signed_block, Validation::Enabled, context) }) 19 | } 20 | } 21 | } 22 | handler => unreachable!("no tests for {handler}"), 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ethereum-consensus/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | //! This module contains high-level types that intend to wrap each of the 2 | //! types defined across all forks. 3 | //! 4 | //! For example, a `BeaconBlock` enum type that contains a variant for each 5 | //! defined fork `phase0`, `altair`, `bellatrix`, `capella`, and onwards. 6 | 7 | mod beacon_block; 8 | mod beacon_block_body; 9 | mod beacon_state; 10 | mod blinded_beacon_block; 11 | mod blinded_beacon_block_body; 12 | mod execution_payload; 13 | mod execution_payload_header; 14 | mod presets; 15 | mod signed_beacon_block; 16 | mod signed_blinded_beacon_block; 17 | 18 | pub use beacon_block::*; 19 | pub use beacon_block_body::*; 20 | pub use beacon_state::*; 21 | pub use blinded_beacon_block::*; 22 | pub use blinded_beacon_block_body::*; 23 | pub use execution_payload::*; 24 | pub use execution_payload_header::*; 25 | pub use signed_beacon_block::*; 26 | pub use signed_blinded_beacon_block::*; 27 | 28 | pub use presets::{mainnet, minimal}; 29 | -------------------------------------------------------------------------------- /ethereum-consensus/src/domains.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy)] 2 | pub enum DomainType { 3 | BeaconProposer, // 0 4 | BeaconAttester, // 1 5 | Randao, // 2 6 | Deposit, // 3 7 | VoluntaryExit, // 4 8 | SelectionProof, // 5 9 | AggregateAndProof, // 6 10 | SyncCommittee, // 7 11 | SyncCommitteeSelectionProof, // 8 12 | ContributionAndProof, // 9 13 | BlsToExecutionChange, // A 14 | Consolidation, // B 15 | ApplicationMask, 16 | ApplicationBuilder, 17 | } 18 | 19 | impl DomainType { 20 | pub fn as_bytes(&self) -> [u8; 4] { 21 | match self { 22 | Self::ApplicationMask => [0, 0, 0, 1], 23 | Self::ApplicationBuilder => [0, 0, 0, 1], 24 | _ => { 25 | let data = *self as u32; 26 | data.to_le_bytes() 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /beacon-api-client/src/cli/config.rs: -------------------------------------------------------------------------------- 1 | use crate::types::StateId; 2 | use clap::{Args, Parser, Subcommand}; 3 | 4 | #[derive(Debug, Parser)] 5 | #[command(author, version, about, long_about = None)] 6 | #[command(propagate_version = true)] 7 | pub struct CliConfig { 8 | #[arg(long)] 9 | pub endpoint: String, 10 | #[command(subcommand)] 11 | pub namespace: Namespace, 12 | } 13 | 14 | #[derive(Debug, Subcommand)] 15 | pub enum Namespace { 16 | #[clap(subcommand)] 17 | Beacon(BeaconMethod), 18 | } 19 | 20 | #[derive(Debug, Subcommand)] 21 | pub enum BeaconMethod { 22 | Genesis, 23 | Root(StateIdArg), 24 | } 25 | 26 | #[derive(Args, Debug)] 27 | pub struct StateIdArg { 28 | #[arg( 29 | value_parser = clap::value_parser!(StateId), 30 | long_help = "Identifier for the state under consideration. Possible values are: 31 | head 32 | genesis 33 | finalized 34 | justified 35 | 36 | ", 37 | )] 38 | pub state_id: StateId, 39 | } 40 | -------------------------------------------------------------------------------- /ethereum-consensus/src/capella/helpers.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | phase0::validator::Validator, 3 | primitives::{Epoch, Gwei, ETH1_ADDRESS_WITHDRAWAL_PREFIX}, 4 | state_transition::Context, 5 | }; 6 | 7 | pub fn has_eth1_withdrawal_credential(validator: &Validator) -> bool { 8 | validator.withdrawal_credentials[0] == ETH1_ADDRESS_WITHDRAWAL_PREFIX 9 | } 10 | 11 | pub fn is_fully_withdrawable_validator(validator: &Validator, balance: Gwei, epoch: Epoch) -> bool { 12 | has_eth1_withdrawal_credential(validator) && 13 | validator.withdrawable_epoch <= epoch && 14 | balance > 0 15 | } 16 | 17 | pub fn is_partially_withdrawable_validator( 18 | validator: &Validator, 19 | balance: Gwei, 20 | context: &Context, 21 | ) -> bool { 22 | let has_max_effective_balance = validator.effective_balance == context.max_effective_balance; 23 | let has_excess_balance = balance > context.max_effective_balance; 24 | has_eth1_withdrawal_credential(validator) && has_max_effective_balance && has_excess_balance 25 | } 26 | -------------------------------------------------------------------------------- /ethereum-consensus/src/builder/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | phase0::compute_domain, 3 | primitives::{BlsPublicKey, BlsSignature, Domain, DomainType, ExecutionAddress}, 4 | ssz::prelude::*, 5 | state_transition::Context, 6 | Error, 7 | }; 8 | 9 | #[derive(Debug, Clone, Default, SimpleSerialize, serde::Serialize, serde::Deserialize)] 10 | pub struct ValidatorRegistration { 11 | pub fee_recipient: ExecutionAddress, 12 | #[serde(with = "crate::serde::as_str")] 13 | pub gas_limit: u64, 14 | #[serde(with = "crate::serde::as_str")] 15 | pub timestamp: u64, 16 | #[serde(rename = "pubkey")] 17 | pub public_key: BlsPublicKey, 18 | } 19 | 20 | #[derive(Debug, Clone, Default, SimpleSerialize, serde::Serialize, serde::Deserialize)] 21 | pub struct SignedValidatorRegistration { 22 | pub message: ValidatorRegistration, 23 | pub signature: BlsSignature, 24 | } 25 | 26 | pub fn compute_builder_domain(context: &Context) -> Result { 27 | let domain_type = DomainType::ApplicationBuilder; 28 | compute_domain(domain_type, None, None, context) 29 | } 30 | -------------------------------------------------------------------------------- /beacon-api-client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "beacon-api-client" 3 | version = "0.1.0" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [features] 10 | default = ["cli", "native-tls"] 11 | cli = ["clap"] 12 | rustls = ["reqwest/rustls-tls", "mev-share-sse/rustls"] 13 | native-tls = ["reqwest/default-tls", "mev-share-sse/native-tls"] 14 | 15 | [dependencies] 16 | tokio = { workspace = true } 17 | tracing = { workspace = true } 18 | reqwest = { workspace = true } 19 | url = { workspace = true } 20 | http = { workspace = true } 21 | mev-share-sse = { workspace = true } 22 | serde = { workspace = true } 23 | serde_json = { workspace = true } 24 | itertools = { workspace = true } 25 | clap = { workspace = true, optional = true } 26 | thiserror = { workspace = true } 27 | ethereum-consensus = { path = "../ethereum-consensus" } 28 | 29 | [dev-dependencies] 30 | dotenv = "0.15.0" 31 | futures-util = "0.3.29" 32 | tracing-subscriber = { version = "0.3", features = ["env-filter"] } 33 | hex = { workspace = true } 34 | -------------------------------------------------------------------------------- /spec-tests/runners/merkle_proof.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | runners::{ 3 | gen_exec, gen_match_for, 4 | light_client::{load_test, run_test, Proof}, 5 | }, 6 | test_case::TestCase, 7 | test_utils::Error, 8 | }; 9 | 10 | pub fn dispatch(test: &TestCase) -> Result<(), Error> { 11 | match test.meta.handler.0.as_str() { 12 | "single_merkle_proof" => { 13 | gen_match_for! { 14 | test, 15 | (mainnet, deneb), 16 | (minimal, deneb) 17 | { 18 | gen_exec! { 19 | test, 20 | load_test, 21 | |(object, proof): (spec::BeaconBlockBody, Proof), _| { 22 | // NOTE: `0` index is hard-coded in test generator 23 | let path = &["blob_kzg_commitments".into(), 0.into()]; 24 | run_test(object, path, &proof) 25 | } 26 | } 27 | } 28 | } 29 | } 30 | handler => unreachable!("no tests for {handler}"), 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Alex Stokes 4 | 5 | Permission is hereby granted, free of charge, to any 6 | person obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without 9 | limitation the rights to use, copy, modify, merge, 10 | publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software 12 | is furnished to do so, subject to the following 13 | conditions: 14 | 15 | The above copyright notice and this permission notice 16 | shall be included in all copies or substantial portions 17 | of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 20 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 21 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 22 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 23 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 25 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 26 | IN CONNECTION WITH THE SOFTWARE O THE USE OR OTHER 27 | DEALINGS IN THE SOFTWARE.R 28 | -------------------------------------------------------------------------------- /ethereum-consensus/src/execution_engine.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | error::{Error, ExecutionEngineError}, 3 | state_transition::Result, 4 | }; 5 | 6 | /// `PayloadRequest` abstracts over the data sent to the `ExecutionEngine`. 7 | /// NOTE: just a "marker" trait for now, until we have more substantial support for execution. 8 | pub trait PayloadRequest {} 9 | 10 | /// `ExecutionEngine` abstracts over the interface between consensus and execution client. 11 | pub trait ExecutionEngine { 12 | /// Verify the new payload and associated data contained in `new_payload_request`. 13 | /// Either return `Err` describing a failure or `Ok(())` if validation succeeds. 14 | fn verify_and_notify_new_payload( 15 | &self, 16 | new_payload_request: &impl PayloadRequest, 17 | ) -> Result<()>; 18 | } 19 | 20 | /// A "no-op" implementation that succeeds for `true` or fails for `false`. 21 | /// Useful for mocking the execution engine behavior. 22 | impl ExecutionEngine for bool { 23 | fn verify_and_notify_new_payload(&self, _: &impl PayloadRequest) -> Result<()> { 24 | self.then_some(()).ok_or(Error::ExecutionEngine(ExecutionEngineError::InvalidPayload)) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ethereum-consensus/src/signing.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | crypto::{self, SecretKey}, 3 | primitives::{BlsPublicKey, BlsSignature, Domain, Root}, 4 | ssz::prelude::*, 5 | Error, 6 | }; 7 | 8 | #[derive(Default, Debug, SimpleSerialize)] 9 | pub struct SigningData { 10 | pub object_root: Root, 11 | pub domain: Domain, 12 | } 13 | 14 | pub fn compute_signing_root( 15 | ssz_object: &T, 16 | domain: Domain, 17 | ) -> Result { 18 | let object_root = ssz_object.hash_tree_root()?; 19 | 20 | let s = SigningData { object_root, domain }; 21 | s.hash_tree_root().map_err(Error::Merkleization) 22 | } 23 | 24 | pub fn sign_with_domain( 25 | data: &T, 26 | signing_key: &SecretKey, 27 | domain: Domain, 28 | ) -> Result { 29 | let signing_root = compute_signing_root(data, domain)?; 30 | Ok(signing_key.sign(signing_root.as_ref())) 31 | } 32 | 33 | pub fn verify_signed_data( 34 | data: &T, 35 | signature: &BlsSignature, 36 | public_key: &BlsPublicKey, 37 | domain: Domain, 38 | ) -> Result<(), Error> { 39 | let signing_root = compute_signing_root(data, domain)?; 40 | crypto::verify_signature(public_key, signing_root.as_ref(), signature).map_err(Into::into) 41 | } 42 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/blobs/mod.rs: -------------------------------------------------------------------------------- 1 | mod bundler; 2 | mod command; 3 | mod decode; 4 | mod encode; 5 | mod framing; 6 | 7 | pub use command::Command; 8 | use ethereum_consensus::{deneb as spec, Error as ConsensusError}; 9 | use thiserror::Error; 10 | 11 | pub(crate) const BYTES_PER_BLOB: usize = spec::mainnet::BYTES_PER_BLOB; 12 | pub(crate) const BYTES_PER_FIELD_ELEMENT: usize = 13 | ethereum_consensus::crypto::kzg::BYTES_PER_FIELD_ELEMENT; 14 | // Number of bits in a valid field element. 15 | pub(crate) const BITS_PER_FIELD_ELEMENT: usize = 254; 16 | 17 | pub(crate) type Blob = spec::mainnet::Blob; 18 | 19 | #[derive(Debug, Error)] 20 | pub enum Error { 21 | #[error("data is not a field element")] 22 | InvalidFieldElement, 23 | #[error("sized framing was requested but decoded stream is not large enough for frame header")] 24 | ExpectedHeaderForSizedFraming, 25 | #[error("sized framing was requested but the encoded version is not supported")] 26 | UnsupportedSizedFramingVersion, 27 | #[error("sized framing was requested but the encoded size exceeds the maximum possible size")] 28 | InvalidPayloadSize, 29 | #[error(transparent)] 30 | Json(#[from] serde_json::Error), 31 | #[error(transparent)] 32 | Io(#[from] std::io::Error), 33 | #[error(transparent)] 34 | Consensus(#[from] ConsensusError), 35 | } 36 | -------------------------------------------------------------------------------- /ethereum-consensus/examples/read_ssz.rs: -------------------------------------------------------------------------------- 1 | use alloy_eips::Decodable2718; 2 | use ethereum_consensus::deneb::mainnet as spec; 3 | use reth_ethereum_primitives::{TransactionSigned, TxType}; 4 | use ssz_rs::prelude::*; 5 | use std::{env, fs}; 6 | 7 | fn main() { 8 | // NOTE: expects a path to a SSZ-encoded signed beacon block 9 | let args = env::args().collect::>(); 10 | let path = args.last().unwrap(); 11 | let f = fs::read(path).unwrap(); 12 | let mut block = spec::SignedBeaconBlock::deserialize(&f).unwrap(); 13 | dbg!(block.message.slot); 14 | dbg!(String::from_utf8(block.message.body.graffiti.to_vec()).unwrap()); 15 | dbg!(String::from_utf8(block.message.body.execution_payload.extra_data.to_vec()).unwrap()); 16 | dbg!(block.message.body.execution_payload.blob_gas_used); 17 | dbg!(&block.message.body.blob_kzg_commitments); 18 | for commitment in block.message.body.blob_kzg_commitments.iter() { 19 | let versioned_hash = spec::kzg_commitment_to_versioned_hash(commitment); 20 | dbg!(versioned_hash); 21 | } 22 | for txn in block.message.body.execution_payload.transactions.iter_mut() { 23 | let txn = TransactionSigned::decode_2718(&mut txn.as_ref()).unwrap(); 24 | if matches!(txn.transaction().tx_type(), TxType::Eip4844) { 25 | dbg!(txn); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ethereum-consensus/src/deneb/execution_engine.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | deneb::{blob_sidecar::VersionedHash, execution_payload::ExecutionPayload}, 3 | execution_engine::PayloadRequest, 4 | primitives::Root, 5 | }; 6 | 7 | pub struct NewPayloadRequest< 8 | const BYTES_PER_LOGS_BLOOM: usize, 9 | const MAX_EXTRA_DATA_BYTES: usize, 10 | const MAX_BYTES_PER_TRANSACTION: usize, 11 | const MAX_TRANSACTIONS_PER_PAYLOAD: usize, 12 | const MAX_WITHDRAWALS_PER_PAYLOAD: usize, 13 | > { 14 | pub execution_payload: ExecutionPayload< 15 | BYTES_PER_LOGS_BLOOM, 16 | MAX_EXTRA_DATA_BYTES, 17 | MAX_BYTES_PER_TRANSACTION, 18 | MAX_TRANSACTIONS_PER_PAYLOAD, 19 | MAX_WITHDRAWALS_PER_PAYLOAD, 20 | >, 21 | pub versioned_hashes: Vec, 22 | pub parent_beacon_block_root: Root, 23 | } 24 | 25 | impl< 26 | const BYTES_PER_LOGS_BLOOM: usize, 27 | const MAX_EXTRA_DATA_BYTES: usize, 28 | const MAX_BYTES_PER_TRANSACTION: usize, 29 | const MAX_TRANSACTIONS_PER_PAYLOAD: usize, 30 | const MAX_WITHDRAWALS_PER_PAYLOAD: usize, 31 | > PayloadRequest 32 | for NewPayloadRequest< 33 | BYTES_PER_LOGS_BLOOM, 34 | MAX_EXTRA_DATA_BYTES, 35 | MAX_BYTES_PER_TRANSACTION, 36 | MAX_TRANSACTIONS_PER_PAYLOAD, 37 | MAX_WITHDRAWALS_PER_PAYLOAD, 38 | > 39 | { 40 | } 41 | -------------------------------------------------------------------------------- /ethereum-consensus/examples/state_transition_across_multiple_forks.rs: -------------------------------------------------------------------------------- 1 | use ethereum_consensus::{ 2 | ssz::prelude::*, 3 | state_transition::mainnet::{Context, Executor}, 4 | types::mainnet::{BeaconState, SignedBeaconBlock}, 5 | }; 6 | use std::error::Error; 7 | 8 | fn main() -> std::result::Result<(), Box> { 9 | println!("this example illustrates how to use the codebase when applying state transitions"); 10 | println!("note: the code currently does not run to the end as the input data is not correct"); 11 | 12 | let genesis_state = BeaconState::Phase0(Default::default()); 13 | let context = Context::for_mainnet(); 14 | let mut executor = Executor::new(genesis_state, context); 15 | 16 | let mut block = SignedBeaconBlock::Phase0(Default::default()); 17 | *block.message_mut().slot_mut() = 1; 18 | executor.apply_block(&block)?; 19 | 20 | let mut block = SignedBeaconBlock::Altair(Default::default()); 21 | *block.message_mut().slot_mut() = 22 | executor.context.altair_fork_epoch * executor.context.slots_per_epoch; 23 | executor.apply_block(&block)?; 24 | 25 | let mut block = SignedBeaconBlock::Bellatrix(Default::default()); 26 | *block.message_mut().slot_mut() = 27 | executor.context.bellatrix_fork_epoch * executor.context.slots_per_epoch; 28 | executor.apply_block(&block)?; 29 | 30 | let state = executor.state.bellatrix().unwrap(); 31 | let state_root = state.hash_tree_root()?; 32 | dbg!(state_root); 33 | Ok(()) 34 | } 35 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/presets/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod mainnet; 2 | pub mod minimal; 3 | 4 | use crate::primitives::{Epoch, Gwei, Slot}; 5 | 6 | pub struct Preset { 7 | pub max_committees_per_slot: u64, 8 | pub target_committee_size: u64, 9 | pub max_validators_per_committee: usize, 10 | pub shuffle_round_count: u64, 11 | pub hysteresis_quotient: u64, 12 | pub hysteresis_downward_multiplier: u64, 13 | pub hysteresis_upward_multiplier: u64, 14 | pub min_deposit_amount: Gwei, 15 | pub max_effective_balance: Gwei, 16 | pub effective_balance_increment: Gwei, 17 | pub min_attestation_inclusion_delay: Slot, 18 | pub slots_per_epoch: Slot, 19 | pub min_seed_lookahead: Epoch, 20 | pub max_seed_lookahead: Epoch, 21 | pub min_epochs_to_inactivity_penalty: Epoch, 22 | pub epochs_per_eth1_voting_period: Epoch, 23 | pub slots_per_historical_root: Slot, 24 | pub epochs_per_historical_vector: Epoch, 25 | pub epochs_per_slashings_vector: Epoch, 26 | pub historical_roots_limit: usize, 27 | pub validator_registry_limit: usize, 28 | pub base_reward_factor: u64, 29 | pub whistleblower_reward_quotient: u64, 30 | pub proposer_reward_quotient: u64, 31 | pub inactivity_penalty_quotient: u64, 32 | pub min_slashing_penalty_quotient: u64, 33 | pub proportional_slashing_multiplier: u64, 34 | pub max_proposer_slashings: usize, 35 | pub max_attester_slashings: usize, 36 | pub max_attestations: usize, 37 | pub max_deposits: usize, 38 | pub max_voluntary_exits: usize, 39 | } 40 | -------------------------------------------------------------------------------- /spec-tests/test_utils.rs: -------------------------------------------------------------------------------- 1 | use ethereum_consensus::Error as SpecError; 2 | use serde::Deserialize; 3 | use std::{fs::File, io::Read, path::Path}; 4 | use thiserror::Error; 5 | 6 | #[derive(Debug, Error)] 7 | pub enum Error { 8 | #[error(transparent)] 9 | Spec(#[from] SpecError), 10 | #[error("state did not match expected")] 11 | InvalidState, 12 | #[error("test was expected to error but did not")] 13 | Expected, 14 | #[error("internal error; you should not see this")] 15 | InternalContinue, 16 | } 17 | 18 | pub fn load_yaml Deserialize<'de>>(path: &str) -> T { 19 | let mut file = File::open(path).expect("File does not exist"); 20 | let test_case: Result = serde_yaml::from_reader(&mut file); 21 | match test_case { 22 | Ok(test_case) => test_case, 23 | Err(err) => { 24 | let content = std::fs::read_to_string(path).unwrap(); 25 | panic!("{err} from {content} at {path:?}") 26 | } 27 | } 28 | } 29 | 30 | pub fn load_snappy_ssz_bytes(path: &Path) -> Vec { 31 | let mut file = File::open(path).unwrap(); 32 | let mut data = vec![]; 33 | file.read_to_end(&mut data).unwrap(); 34 | 35 | let mut decoder = snap::raw::Decoder::new(); 36 | decoder.decompress_vec(&data).unwrap() 37 | } 38 | 39 | pub fn load_snappy_ssz(path: &str) -> Option { 40 | let path = Path::new(path); 41 | if !path.exists() { 42 | return None 43 | } 44 | let buffer = load_snappy_ssz_bytes(path); 45 | 46 | let result = ::deserialize(&buffer); 47 | 48 | result.ok() 49 | } 50 | -------------------------------------------------------------------------------- /ethereum-consensus/examples/sketch.rs: -------------------------------------------------------------------------------- 1 | use ethereum_consensus::{ 2 | altair::mainnet as altair, 3 | bellatrix::mainnet as bellatrix, 4 | phase0::mainnet as phase0, 5 | state_transition::{Context, Validation}, 6 | }; 7 | 8 | fn main() { 9 | let context = Context::for_mainnet(); 10 | 11 | // phase0 transition 12 | let mut state = phase0::BeaconState::default(); 13 | let mut signed_block = phase0::SignedBeaconBlock::default(); 14 | signed_block.message.slot = 1; 15 | 16 | let previous_epoch = phase0::get_previous_epoch(&state, &context); 17 | dbg!(previous_epoch); 18 | 19 | let _ = phase0::state_transition(&mut state, &signed_block, Validation::Enabled, &context); 20 | dbg!(state.fork); 21 | 22 | // altair transition 23 | let mut state = altair::BeaconState::default(); 24 | let mut signed_block = altair::SignedBeaconBlock::default(); 25 | state.slot = 32; 26 | signed_block.message.slot = 33; 27 | 28 | let current_epoch = altair::get_current_epoch(&state, &context); 29 | dbg!(current_epoch); 30 | 31 | let _ = altair::state_transition(&mut state, &signed_block, Validation::Enabled, &context); 32 | dbg!(state.fork); 33 | 34 | // bellatrix transition 35 | let mut state = bellatrix::BeaconState::default(); 36 | let mut signed_block = bellatrix::SignedBeaconBlock::default(); 37 | state.slot = 32; 38 | signed_block.message.slot = 33; 39 | 40 | let current_epoch = bellatrix::get_current_epoch(&state, &context); 41 | dbg!(current_epoch); 42 | 43 | let _ = bellatrix::state_transition(&mut state, &signed_block, Validation::Enabled, &context); 44 | dbg!(state.fork); 45 | } 46 | -------------------------------------------------------------------------------- /ethereum-consensus/src/state_transition/presets/mainnet.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::mainnet::SYNC_COMMITTEE_SIZE, 3 | bellatrix::mainnet::{ 4 | BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, 5 | MAX_TRANSACTIONS_PER_PAYLOAD, 6 | }, 7 | capella::mainnet::{MAX_BLS_TO_EXECUTION_CHANGES, MAX_WITHDRAWALS_PER_PAYLOAD}, 8 | deneb::mainnet::MAX_BLOB_COMMITMENTS_PER_BLOCK, 9 | phase0::mainnet::{ 10 | EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, 11 | HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, 12 | MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, 13 | PENDING_ATTESTATIONS_BOUND, SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, 14 | }, 15 | state_transition, 16 | }; 17 | 18 | pub use crate::Error; 19 | pub use state_transition::{Context, Validation}; 20 | 21 | pub type Executor = state_transition::Executor< 22 | SLOTS_PER_HISTORICAL_ROOT, 23 | HISTORICAL_ROOTS_LIMIT, 24 | ETH1_DATA_VOTES_BOUND, 25 | VALIDATOR_REGISTRY_LIMIT, 26 | EPOCHS_PER_HISTORICAL_VECTOR, 27 | EPOCHS_PER_SLASHINGS_VECTOR, 28 | MAX_VALIDATORS_PER_COMMITTEE, 29 | PENDING_ATTESTATIONS_BOUND, 30 | SYNC_COMMITTEE_SIZE, 31 | BYTES_PER_LOGS_BLOOM, 32 | MAX_EXTRA_DATA_BYTES, 33 | MAX_BYTES_PER_TRANSACTION, 34 | MAX_TRANSACTIONS_PER_PAYLOAD, 35 | MAX_PROPOSER_SLASHINGS, 36 | MAX_ATTESTER_SLASHINGS, 37 | MAX_ATTESTATIONS, 38 | MAX_DEPOSITS, 39 | MAX_VOLUNTARY_EXITS, 40 | MAX_WITHDRAWALS_PER_PAYLOAD, 41 | MAX_BLS_TO_EXECUTION_CHANGES, 42 | MAX_BLOB_COMMITMENTS_PER_BLOCK, 43 | >; 44 | -------------------------------------------------------------------------------- /ethereum-consensus/src/state_transition/presets/minimal.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::minimal::SYNC_COMMITTEE_SIZE, 3 | bellatrix::minimal::{ 4 | BYTES_PER_LOGS_BLOOM, MAX_BYTES_PER_TRANSACTION, MAX_EXTRA_DATA_BYTES, 5 | MAX_TRANSACTIONS_PER_PAYLOAD, 6 | }, 7 | capella::minimal::{MAX_BLS_TO_EXECUTION_CHANGES, MAX_WITHDRAWALS_PER_PAYLOAD}, 8 | deneb::minimal::MAX_BLOB_COMMITMENTS_PER_BLOCK, 9 | phase0::minimal::{ 10 | EPOCHS_PER_HISTORICAL_VECTOR, EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, 11 | HISTORICAL_ROOTS_LIMIT, MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, 12 | MAX_PROPOSER_SLASHINGS, MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, 13 | PENDING_ATTESTATIONS_BOUND, SLOTS_PER_HISTORICAL_ROOT, VALIDATOR_REGISTRY_LIMIT, 14 | }, 15 | state_transition, 16 | }; 17 | 18 | pub use crate::Error; 19 | pub use state_transition::{Context, Validation}; 20 | 21 | pub type Executor = state_transition::Executor< 22 | SLOTS_PER_HISTORICAL_ROOT, 23 | HISTORICAL_ROOTS_LIMIT, 24 | ETH1_DATA_VOTES_BOUND, 25 | VALIDATOR_REGISTRY_LIMIT, 26 | EPOCHS_PER_HISTORICAL_VECTOR, 27 | EPOCHS_PER_SLASHINGS_VECTOR, 28 | MAX_VALIDATORS_PER_COMMITTEE, 29 | PENDING_ATTESTATIONS_BOUND, 30 | SYNC_COMMITTEE_SIZE, 31 | BYTES_PER_LOGS_BLOOM, 32 | MAX_EXTRA_DATA_BYTES, 33 | MAX_BYTES_PER_TRANSACTION, 34 | MAX_TRANSACTIONS_PER_PAYLOAD, 35 | MAX_PROPOSER_SLASHINGS, 36 | MAX_ATTESTER_SLASHINGS, 37 | MAX_ATTESTATIONS, 38 | MAX_DEPOSITS, 39 | MAX_VOLUNTARY_EXITS, 40 | MAX_WITHDRAWALS_PER_PAYLOAD, 41 | MAX_BLS_TO_EXECUTION_CHANGES, 42 | MAX_BLOB_COMMITMENTS_PER_BLOCK, 43 | >; 44 | -------------------------------------------------------------------------------- /beacon-api-client/src/api_error.rs: -------------------------------------------------------------------------------- 1 | use http::StatusCode; 2 | use serde::{Deserialize, Serialize}; 3 | use std::{error::Error, fmt}; 4 | 5 | // NOTE: `IndexedError` must come before `ErrorMessage` so 6 | // the `serde(untagged)` machinery does not greedily match it first. 7 | #[derive(Serialize, Deserialize, Debug)] 8 | #[serde(untagged)] 9 | pub enum ApiError { 10 | IndexedError { 11 | #[serde(with = "crate::serde::as_u16")] 12 | code: StatusCode, 13 | message: String, 14 | failures: Vec, 15 | }, 16 | ErrorMessage { 17 | #[serde(with = "crate::serde::as_u16")] 18 | code: StatusCode, 19 | message: String, 20 | }, 21 | } 22 | 23 | #[derive(Serialize, Deserialize, Debug)] 24 | pub struct IndexedError { 25 | index: usize, 26 | message: String, 27 | } 28 | 29 | impl fmt::Display for ApiError { 30 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 | match self { 32 | Self::ErrorMessage { message, .. } => { 33 | write!(f, "{message}") 34 | } 35 | Self::IndexedError { message, failures, .. } => { 36 | write!(f, "{message}: ")?; 37 | for failure in failures { 38 | write!(f, "{failure:?}, ")?; 39 | } 40 | Ok(()) 41 | } 42 | } 43 | } 44 | } 45 | 46 | impl Error for ApiError {} 47 | 48 | impl<'a> TryFrom<(u16, &'a str)> for ApiError { 49 | type Error = http::status::InvalidStatusCode; 50 | 51 | fn try_from((code, message): (u16, &'a str)) -> Result { 52 | let code = StatusCode::from_u16(code)?; 53 | Ok(Self::ErrorMessage { code, message: message.to_string() }) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /ethereum-consensus/src/electra/execution_engine.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | deneb::blob_sidecar::VersionedHash, electra::ExecutionPayload, 3 | execution_engine::PayloadRequest, primitives::Root, 4 | }; 5 | 6 | pub struct NewPayloadRequest< 7 | const BYTES_PER_LOGS_BLOOM: usize, 8 | const MAX_EXTRA_DATA_BYTES: usize, 9 | const MAX_BYTES_PER_TRANSACTION: usize, 10 | const MAX_TRANSACTIONS_PER_PAYLOAD: usize, 11 | const MAX_WITHDRAWALS_PER_PAYLOAD: usize, 12 | const MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: usize, 13 | const MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: usize, 14 | > { 15 | pub execution_payload: ExecutionPayload< 16 | BYTES_PER_LOGS_BLOOM, 17 | MAX_EXTRA_DATA_BYTES, 18 | MAX_BYTES_PER_TRANSACTION, 19 | MAX_TRANSACTIONS_PER_PAYLOAD, 20 | MAX_WITHDRAWALS_PER_PAYLOAD, 21 | MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD, 22 | MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, 23 | >, 24 | pub versioned_hashes: Vec, 25 | pub parent_beacon_block_root: Root, 26 | } 27 | 28 | impl< 29 | const BYTES_PER_LOGS_BLOOM: usize, 30 | const MAX_EXTRA_DATA_BYTES: usize, 31 | const MAX_BYTES_PER_TRANSACTION: usize, 32 | const MAX_TRANSACTIONS_PER_PAYLOAD: usize, 33 | const MAX_WITHDRAWALS_PER_PAYLOAD: usize, 34 | const MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: usize, 35 | const MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: usize, 36 | > PayloadRequest 37 | for NewPayloadRequest< 38 | BYTES_PER_LOGS_BLOOM, 39 | MAX_EXTRA_DATA_BYTES, 40 | MAX_BYTES_PER_TRANSACTION, 41 | MAX_TRANSACTIONS_PER_PAYLOAD, 42 | MAX_WITHDRAWALS_PER_PAYLOAD, 43 | MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD, 44 | MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, 45 | > 46 | { 47 | } 48 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI suite 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@v3 15 | 16 | - name: Read toolchain file 17 | id: rust-toolchain 18 | run: | 19 | RUST_TOOLCHAIN=$(grep 'channel' rust-toolchain.toml | awk '{split($0,a," = "); print a[2]}' | tr -d '"') 20 | echo "RUST_TOOLCHAIN=$RUST_TOOLCHAIN" >> $GITHUB_OUTPUT 21 | shell: bash 22 | 23 | - name: Install toolchain 24 | uses: dtolnay/rust-toolchain@master 25 | with: 26 | toolchain: ${{ steps.rust-toolchain.outputs.RUST_TOOLCHAIN }} 27 | 28 | - name: Rust cache 29 | uses: Swatinem/rust-cache@v2 30 | with: 31 | cache-on-failure: true 32 | 33 | - name: Build 34 | run: cargo build --all-targets --all-features --workspace --verbose 35 | 36 | - name: Run tests 37 | run: cargo test --all-features --all-targets --workspace --exclude spec-tests --verbose 38 | 39 | lint: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - name: Checkout sources 43 | uses: actions/checkout@v3 44 | 45 | - name: Install toolchain 46 | uses: dtolnay/rust-toolchain@nightly 47 | with: 48 | components: rustfmt, clippy 49 | 50 | - name: Rust cache 51 | uses: Swatinem/rust-cache@v2 52 | with: 53 | cache-on-failure: true 54 | 55 | - name: Check format 56 | run: cargo +nightly fmt --all --check 57 | 58 | - name: Check clippy 59 | run: cargo +nightly clippy --all-targets --all-features --workspace --verbose -- -D warnings 60 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | 4 | members = ["beacon-api-client", "ethereum-consensus", "spec-gen", "spec-tests"] 5 | 6 | [workspace.dependencies] 7 | tokio = { version = "1.37.0", features = ["full"] } 8 | tokio-stream = "0.1.15" 9 | tracing = "0.1" 10 | reqwest = { version = "0.11.10", default-features = false, features = ["json"] } 11 | url = "2.2.2" 12 | http = "0.2.7" 13 | mev-share-sse = { git = "https://github.com/paradigmxyz/mev-share-rs", rev = "9eb2b0138ab3202b9eb3af4b19c7b3bf40b0faa8", default-features = false } 14 | serde = { version = "1.0", features = ["derive"] } 15 | serde_json = "1.0.81" 16 | serde_yaml = "0.8" 17 | itertools = "0.10.3" 18 | thiserror = "1.0.30" 19 | hex = "0.4.3" 20 | ssz_rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "84ef2b71aa004f6767420badb42c902ad56b8b72" } 21 | blst = "0.3.11" 22 | rand = "0.8.4" 23 | sha2 = "0.10.8" 24 | integer-sqrt = "0.1.5" 25 | enr = "0.6.2" 26 | multihash = { version = "0.16", default-features = false, features = [ 27 | "std", 28 | "multihash-impl", 29 | "identity", 30 | "sha2", 31 | ] } 32 | multiaddr = "0.14.0" 33 | c-kzg = "2.1.0" 34 | bs58 = "0.4.0" 35 | eyre = "0.6.8" 36 | bip39 = "2.0.0" 37 | rand_core = { version = "0.6", features = ["std"] } 38 | rayon = "1.8.0" 39 | hkdf = "0.12.3" 40 | ruint = "1.11.1" 41 | uuid = { version = "1.4.1", features = ["v4", "fast-rng", "serde"] } 42 | scrypt = "0.11.0" 43 | aes = "0.8.3" 44 | ctr = "0.9.2" 45 | base64 = "0.21.4" 46 | unicode-normalization = "0.1.22" 47 | bitvec = "1.0.1" 48 | syn = { version = "1.0.98", features = [ 49 | "full", 50 | "visit", 51 | "visit-mut", 52 | "extra-traits", 53 | ] } 54 | prettyplease = "0.1.10" 55 | quote = "1.0.18" 56 | clap = { version = "4.5.3", features = ["derive"] } 57 | convert_case = "0.6.0" 58 | walkdir = "2.3.3" 59 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/validator/mod.rs: -------------------------------------------------------------------------------- 1 | mod keys; 2 | mod keystores; 3 | mod mnemonic; 4 | 5 | use clap::{Args, Subcommand}; 6 | 7 | #[derive(Debug, Subcommand)] 8 | pub enum Commands { 9 | Mnemonic, 10 | #[clap( 11 | about = "Generates keystores (with passphrases) that target a format compatible with `lighthouse validator-manager` utility." 12 | )] 13 | GenerateLighthouseKeystores { 14 | #[clap(help = "BIP-39 mnemonic to use following EIP-2334")] 15 | phrase: String, 16 | #[clap(help = "EIP-2334 index to start key generation (inclusive)")] 17 | start: u32, 18 | #[clap(help = "EIP-2334 index to stop key generation (exclusive)")] 19 | end: u32, 20 | }, 21 | } 22 | 23 | #[derive(Debug, Args)] 24 | #[clap(about = "utilities for managing validators")] 25 | pub struct Command { 26 | #[clap(subcommand)] 27 | pub command: Commands, 28 | } 29 | 30 | impl Command { 31 | pub fn execute(self) -> eyre::Result<()> { 32 | match self.command { 33 | Commands::Mnemonic => { 34 | let mnemonic = mnemonic::generate_random_from_system_entropy()?; 35 | println!("{}", mnemonic); 36 | Ok(()) 37 | } 38 | Commands::GenerateLighthouseKeystores { phrase, start, end } => { 39 | let mnemonic = mnemonic::recover_from_phrase(&phrase)?; 40 | let seed = mnemonic::to_seed(mnemonic, None); 41 | let (signing_keys, _withdrawal_keys) = keys::generate(&seed, start, end); 42 | let keystores_with_passphrases = keystores::generate(signing_keys); 43 | println!("{}", serde_json::to_string_pretty(&keystores_with_passphrases).unwrap()); 44 | Ok(()) 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ethereum-consensus/src/ssz/byte_vector.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | serde::{write_bytes_to_lower_hex, write_bytes_to_lower_hex_display}, 3 | ssz::prelude::*, 4 | }; 5 | use std::{ 6 | fmt, 7 | hash::{Hash, Hasher}, 8 | ops::{Deref, DerefMut}, 9 | }; 10 | 11 | #[derive( 12 | Default, 13 | Clone, 14 | PartialEq, 15 | Eq, 16 | PartialOrd, 17 | Ord, 18 | SimpleSerialize, 19 | serde::Serialize, 20 | serde::Deserialize, 21 | )] 22 | pub struct ByteVector(#[serde(with = "crate::serde::as_hex")] Vector); 23 | 24 | impl TryFrom<&[u8]> for ByteVector { 25 | type Error = DeserializeError; 26 | 27 | fn try_from(bytes: &[u8]) -> Result { 28 | ByteVector::::deserialize(bytes) 29 | } 30 | } 31 | 32 | impl Hash for ByteVector { 33 | fn hash(&self, state: &mut H) { 34 | self.as_ref().hash(state); 35 | } 36 | } 37 | 38 | impl fmt::Debug for ByteVector { 39 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 40 | write_bytes_to_lower_hex(f, &self.0) 41 | } 42 | } 43 | 44 | impl fmt::Display for ByteVector { 45 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 46 | write_bytes_to_lower_hex_display(f, self.0.iter()) 47 | } 48 | } 49 | 50 | impl AsRef<[u8]> for ByteVector { 51 | fn as_ref(&self) -> &[u8] { 52 | self.0.as_ref() 53 | } 54 | } 55 | 56 | impl Deref for ByteVector { 57 | type Target = Vector; 58 | 59 | fn deref(&self) -> &Self::Target { 60 | &self.0 61 | } 62 | } 63 | 64 | impl DerefMut for ByteVector { 65 | fn deref_mut(&mut self) -> &mut Self::Target { 66 | &mut self.0 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/blobs/framing.rs: -------------------------------------------------------------------------------- 1 | use crate::blobs::Error; 2 | 3 | pub const SIZED_FRAMING_VERSION: u8 = 0; 4 | pub const HEADER_SIZE: usize = 5; 5 | 6 | /// A `Mode` to indicate how the target data should be packed into blob data. 7 | pub enum Mode { 8 | /// No framing, data is written/read directly from the blob data 9 | Raw, 10 | /// The size of a "payload" is written in-band to the blob data. 11 | /// Supports "lossless" {de,}serialization if the payload data is not 12 | /// a multiple of the blob size. 13 | Sized, 14 | } 15 | 16 | // Returns the header bytes that should prepend the target data in `Sized` framing mode. 17 | // The header consists of one version byte, then a `u32` integer in big-endian encoding containing 18 | // the size of the trailing data. 19 | pub fn sized_header(data_byte_length: usize) -> Result<[u8; HEADER_SIZE], Error> { 20 | let mut header = [0u8; HEADER_SIZE]; 21 | header[0] = SIZED_FRAMING_VERSION; 22 | let size = u32::try_from(data_byte_length).map_err(|_| Error::InvalidPayloadSize)?; 23 | header[1..].copy_from_slice(&size.to_be_bytes()); 24 | Ok(header) 25 | } 26 | 27 | // Attempts to parse a `stream` of bytes assuming they were written to blobs with the `Sized` 28 | // framing mode. 29 | pub fn payload_from_sized(stream: &[u8]) -> Result<&[u8], Error> { 30 | if stream.len() < HEADER_SIZE { 31 | return Err(Error::ExpectedHeaderForSizedFraming) 32 | } 33 | 34 | let (header, payload) = stream.split_at(HEADER_SIZE); 35 | 36 | if header[0] != SIZED_FRAMING_VERSION { 37 | return Err(Error::UnsupportedSizedFramingVersion) 38 | } 39 | let size = u32::from_be_bytes(header[1..5].try_into().expect("correct size bytes")) as usize; 40 | if size >= stream.len() { 41 | return Err(Error::InvalidPayloadSize) 42 | } 43 | 44 | Ok(&payload[..size]) 45 | } 46 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/validator.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | phase0::operations::Attestation, 3 | primitives::{BlsPublicKey, BlsSignature, Bytes32, Epoch, Gwei, Root, ValidatorIndex}, 4 | ssz::prelude::*, 5 | }; 6 | 7 | #[derive( 8 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 9 | )] 10 | pub struct Validator { 11 | #[serde(rename = "pubkey")] 12 | pub public_key: BlsPublicKey, 13 | pub withdrawal_credentials: Bytes32, 14 | #[serde(with = "crate::serde::as_str")] 15 | pub effective_balance: Gwei, 16 | pub slashed: bool, 17 | // Status epochs 18 | #[serde(with = "crate::serde::as_str")] 19 | pub activation_eligibility_epoch: Epoch, 20 | #[serde(with = "crate::serde::as_str")] 21 | pub activation_epoch: Epoch, 22 | #[serde(with = "crate::serde::as_str")] 23 | pub exit_epoch: Epoch, 24 | #[serde(with = "crate::serde::as_str")] 25 | pub withdrawable_epoch: Epoch, 26 | } 27 | 28 | #[derive(Default, Debug, SimpleSerialize, Clone, serde::Serialize, serde::Deserialize)] 29 | pub struct Eth1Block { 30 | pub timestamp: u64, 31 | pub deposit_root: Root, 32 | pub deposit_count: u64, 33 | } 34 | 35 | #[derive(Default, Debug, SimpleSerialize, Clone, serde::Serialize, serde::Deserialize)] 36 | pub struct AggregateAndProof { 37 | #[serde(with = "crate::serde::as_str")] 38 | pub aggregator_index: ValidatorIndex, 39 | pub aggregate: Attestation, 40 | pub selection_proof: BlsSignature, 41 | } 42 | 43 | #[derive(Default, Debug, SimpleSerialize, Clone, serde::Serialize, serde::Deserialize)] 44 | pub struct SignedAggregateAndProof { 45 | pub message: AggregateAndProof, 46 | pub signature: BlsSignature, 47 | } 48 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/validator.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | primitives::{BlsSignature, Root, Slot, ValidatorIndex}, 3 | ssz::prelude::*, 4 | }; 5 | 6 | #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 7 | pub struct SyncCommitteeMessage { 8 | #[serde(with = "crate::serde::as_str")] 9 | pub slot: Slot, 10 | pub beacon_block_root: Root, 11 | #[serde(with = "crate::serde::as_str")] 12 | pub validator_index: ValidatorIndex, 13 | pub signature: BlsSignature, 14 | } 15 | 16 | #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 17 | pub struct SyncCommitteeContribution { 18 | #[serde(with = "crate::serde::as_str")] 19 | pub slot: Slot, 20 | pub beacon_block_root: Root, 21 | #[serde(with = "crate::serde::as_str")] 22 | pub subcommittee_index: u64, 23 | pub aggregation_bits: Bitvector, 24 | pub signature: BlsSignature, 25 | } 26 | 27 | #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 28 | pub struct ContributionAndProof { 29 | #[serde(with = "crate::serde::as_str")] 30 | pub aggregator_index: ValidatorIndex, 31 | pub contribution: SyncCommitteeContribution, 32 | pub selection_proof: BlsSignature, 33 | } 34 | 35 | #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 36 | pub struct SignedContributionAndProof { 37 | pub message: ContributionAndProof, 38 | pub signature: BlsSignature, 39 | } 40 | 41 | #[derive(Debug, Default, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 42 | pub struct SyncAggregatorSelectionData { 43 | pub slot: Slot, 44 | pub subcommittee_index: u64, 45 | } 46 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/blobs/bundler.rs: -------------------------------------------------------------------------------- 1 | use crate::blobs::{Blob, Error}; 2 | use ethereum_consensus::{ 3 | crypto::{kzg as spec, kzg::kzg_settings_with_precompute_arc, PRECOMPUTE}, 4 | deneb, Error as ConsensusError, 5 | }; 6 | use std::io::Read; 7 | 8 | type BlobsBundle = deneb::mainnet::BlobsBundle; 9 | type Commitment = spec::KzgCommitment; 10 | type Proof = spec::KzgProof; 11 | type CommitmentAndProof = (Commitment, Proof); 12 | 13 | pub fn commit_and_prove_blob( 14 | blob: &Blob, 15 | kzg_settings: &spec::KzgSettings, 16 | ) -> Result { 17 | let commitment = spec::blob_to_kzg_commitment(blob, kzg_settings)?; 18 | let proof = spec::compute_blob_kzg_proof(blob, &commitment, kzg_settings)?; 19 | Ok((commitment, proof)) 20 | } 21 | 22 | pub fn bundle(blobs: Vec, kzg_settings: &spec::KzgSettings) -> Result { 23 | let commitments_and_proofs = blobs 24 | .iter() 25 | .map(|blob| commit_and_prove_blob(blob, kzg_settings)) 26 | .collect::, ConsensusError>>()?; 27 | let (commitments, proofs) = commitments_and_proofs.into_iter().unzip(); 28 | let blobs_bundle = BlobsBundle { commitments, proofs, blobs }; 29 | 30 | spec::verify_blob_kzg_proof_batch( 31 | &blobs_bundle.blobs, 32 | &blobs_bundle.commitments, 33 | &blobs_bundle.proofs, 34 | kzg_settings, 35 | ) 36 | .map_err(ConsensusError::from)?; 37 | 38 | Ok(blobs_bundle) 39 | } 40 | 41 | // Assumes a serde_json-encoded array of `Vec` on `reader` and uses the mainnet trusted setup. 42 | pub fn from_reader(reader: impl Read) -> Result { 43 | let kzg_settings = kzg_settings_with_precompute_arc(PRECOMPUTE); 44 | let blobs: Vec = serde_json::from_reader(reader)?; 45 | bundle(blobs, kzg_settings.as_ref()) 46 | } 47 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/blobs/decode.rs: -------------------------------------------------------------------------------- 1 | use crate::blobs::{ 2 | framing::{payload_from_sized, Mode as Framing}, 3 | Blob, Error, BITS_PER_FIELD_ELEMENT, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT, 4 | }; 5 | use bitvec::prelude::*; 6 | use std::io::{Read, Write}; 7 | 8 | const BITS_PER_SERIALIZED_FIELD_ELEMENT: usize = 8 * BYTES_PER_FIELD_ELEMENT; 9 | 10 | pub fn unpack_from_blobs(blobs: &[Blob]) -> Result, Error> { 11 | let mut stream = vec![0u8; blobs.len() * BYTES_PER_BLOB]; 12 | let stream_bits = stream.view_bits_mut::(); 13 | 14 | let mut i = 0; 15 | for blob in blobs { 16 | let blob_bits = blob.as_ref().view_bits::(); 17 | // chunks of serialized field element bits 18 | let mut chunks = blob_bits.chunks_exact(BITS_PER_SERIALIZED_FIELD_ELEMENT); 19 | for chunk in chunks.by_ref() { 20 | // first two-bits are unusable via the big-endian field element encoding 21 | let src = &chunk[2..]; 22 | stream_bits[i * BITS_PER_FIELD_ELEMENT..(i + 1) * BITS_PER_FIELD_ELEMENT] 23 | .copy_from_bitslice(src); 24 | i += 1; 25 | } 26 | 27 | let remainder = chunks.remainder(); 28 | debug_assert!(remainder.is_empty()); 29 | } 30 | 31 | Ok(stream) 32 | } 33 | 34 | // Expects a `Vec` with `serde_json` encoding read from `reader`. 35 | // Writes recovered byte stream to `writer`. 36 | pub fn to_writer_from_json( 37 | reader: impl Read, 38 | mut writer: impl Write, 39 | framing: Framing, 40 | ) -> Result<(), Error> { 41 | let blobs: Vec = serde_json::from_reader(reader)?; 42 | let result = unpack_from_blobs(&blobs)?; 43 | let result = match framing { 44 | Framing::Raw => &result, 45 | Framing::Sized => payload_from_sized(&result)?, 46 | }; 47 | writer.write_all(result)?; 48 | Ok(()) 49 | } 50 | -------------------------------------------------------------------------------- /spec-tests/runners/utils.rs: -------------------------------------------------------------------------------- 1 | use crate::test_utils::{load_snappy_ssz, load_yaml, Error}; 2 | use ethereum_consensus::{state_transition::Context, Error as SpecError}; 3 | use serde::Deserialize; 4 | 5 | #[derive(Deserialize)] 6 | struct BlocksMeta { 7 | blocks_count: usize, 8 | } 9 | 10 | pub(crate) fn load_blocks_test( 11 | test_case_path: &str, 12 | ) -> (S, Option, Vec) { 13 | let path = test_case_path.to_string() + "/pre.ssz_snappy"; 14 | let pre: S = load_snappy_ssz(&path).unwrap(); 15 | 16 | let path = test_case_path.to_string() + "/post.ssz_snappy"; 17 | let post: Option = load_snappy_ssz(&path); 18 | 19 | let path = test_case_path.to_string() + "/meta.yaml"; 20 | let meta: BlocksMeta = load_yaml(&path); 21 | let blocks_count = meta.blocks_count; 22 | 23 | let mut blocks = vec![]; 24 | for i in 0..blocks_count { 25 | let path = format!("{test_case_path}/blocks_{i}.ssz_snappy"); 26 | let block: B = load_snappy_ssz(&path).unwrap(); 27 | blocks.push(block); 28 | } 29 | 30 | (pre, post, blocks) 31 | } 32 | 33 | pub(crate) fn run_blocks_test( 34 | mut pre: S, 35 | post: Option, 36 | mut blocks: Vec, 37 | context: &Context, 38 | exec_fn: F, 39 | ) -> Result<(), Error> 40 | where 41 | F: Fn(&mut S, &mut B, &Context) -> Result<(), SpecError>, 42 | { 43 | let result = blocks 44 | .iter_mut() 45 | .map(|block| exec_fn(&mut pre, block, context)) 46 | .collect::, SpecError>>(); 47 | if let Some(post) = post { 48 | assert!(result.is_ok()); 49 | if pre == post { 50 | Ok(()) 51 | } else { 52 | Err(Error::InvalidState) 53 | } 54 | } else if result.is_err() { 55 | Ok(()) 56 | } else { 57 | Err(Error::Expected) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /ethereum-consensus/src/electra/presets/minimal.rs: -------------------------------------------------------------------------------- 1 | pub use crate::electra::presets::Preset; 2 | use crate::electra::spec; 3 | 4 | pub use spec::*; 5 | 6 | pub const MIN_ACTIVATION_BALANCE: Gwei = 32 * 10u64.pow(9); 7 | pub const MAX_EFFECTIVE_BALANCE_ELECTRA: Gwei = 2048 * 10u64.pow(9); 8 | pub const MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: u64 = 4096; 9 | pub const WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: u64 = 4096; 10 | pub const PENDING_BALANCE_DEPOSITS_LIMIT: usize = 2usize.pow(27); 11 | pub const PENDING_PARTIAL_WITHDRAWALS_LIMIT: usize = 2usize.pow(6); 12 | pub const PENDING_CONSOLIDATIONS_LIMIT: usize = 2usize.pow(6); 13 | pub const MAX_ATTESTER_SLASHINGS_ELECTRA: usize = 1; 14 | pub const MAX_ATTESTATIONS_ELECTRA: usize = 8; 15 | pub const MAX_CONSOLIDATIONS: usize = 1; 16 | pub const MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: usize = 4; 17 | pub const MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: usize = 2; 18 | pub const MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: usize = 1; 19 | 20 | pub const PRESET: Preset = Preset { 21 | min_activation_balance: MIN_ACTIVATION_BALANCE, 22 | max_effective_balance_electra: MAX_EFFECTIVE_BALANCE_ELECTRA, 23 | min_slashing_penalty_quotient_electra: MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA, 24 | whistleblower_reward_quotient_electra: WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA, 25 | pending_balance_deposits_limit: PENDING_BALANCE_DEPOSITS_LIMIT, 26 | pending_partial_withdrawals_limit: PENDING_PARTIAL_WITHDRAWALS_LIMIT, 27 | pending_consolidations_limit: PENDING_CONSOLIDATIONS_LIMIT, 28 | max_attester_slashings_electra: MAX_ATTESTER_SLASHINGS_ELECTRA, 29 | max_attestations_electra: MAX_ATTESTATIONS_ELECTRA, 30 | max_consolidations: MAX_CONSOLIDATIONS, 31 | max_deposit_receipts_per_payload: MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD, 32 | max_withdrawal_requests_per_payload: MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, 33 | max_pending_partials_per_withdrawals_sweep: MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP, 34 | }; 35 | -------------------------------------------------------------------------------- /ethereum-consensus/src/electra/presets/mainnet.rs: -------------------------------------------------------------------------------- 1 | pub use crate::electra::presets::Preset; 2 | use crate::electra::spec; 3 | 4 | pub use spec::*; 5 | 6 | pub const MIN_ACTIVATION_BALANCE: Gwei = 32 * 10u64.pow(9); 7 | pub const MAX_EFFECTIVE_BALANCE_ELECTRA: Gwei = 2048 * 10u64.pow(9); 8 | pub const MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA: u64 = 4096; 9 | pub const WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA: u64 = 4096; 10 | pub const PENDING_BALANCE_DEPOSITS_LIMIT: usize = 2usize.pow(27); 11 | pub const PENDING_PARTIAL_WITHDRAWALS_LIMIT: usize = 2usize.pow(27); 12 | pub const PENDING_CONSOLIDATIONS_LIMIT: usize = 2usize.pow(18); 13 | pub const MAX_ATTESTER_SLASHINGS_ELECTRA: usize = 1; 14 | pub const MAX_ATTESTATIONS_ELECTRA: usize = 8; 15 | pub const MAX_CONSOLIDATIONS: usize = 1; 16 | pub const MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD: usize = 8192; 17 | pub const MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD: usize = 16; 18 | pub const MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP: usize = 8; 19 | 20 | pub const PRESET: Preset = Preset { 21 | min_activation_balance: MIN_ACTIVATION_BALANCE, 22 | max_effective_balance_electra: MAX_EFFECTIVE_BALANCE_ELECTRA, 23 | min_slashing_penalty_quotient_electra: MIN_SLASHING_PENALTY_QUOTIENT_ELECTRA, 24 | whistleblower_reward_quotient_electra: WHISTLEBLOWER_REWARD_QUOTIENT_ELECTRA, 25 | pending_balance_deposits_limit: PENDING_BALANCE_DEPOSITS_LIMIT, 26 | pending_partial_withdrawals_limit: PENDING_PARTIAL_WITHDRAWALS_LIMIT, 27 | pending_consolidations_limit: PENDING_CONSOLIDATIONS_LIMIT, 28 | max_attester_slashings_electra: MAX_ATTESTER_SLASHINGS_ELECTRA, 29 | max_attestations_electra: MAX_ATTESTATIONS_ELECTRA, 30 | max_consolidations: MAX_CONSOLIDATIONS, 31 | max_deposit_receipts_per_payload: MAX_DEPOSIT_RECEIPTS_PER_PAYLOAD, 32 | max_withdrawal_requests_per_payload: MAX_WITHDRAWAL_REQUESTS_PER_PAYLOAD, 33 | max_pending_partials_per_withdrawals_sweep: MAX_PENDING_PARTIALS_PER_WITHDRAWALS_SWEEP, 34 | }; 35 | -------------------------------------------------------------------------------- /ethereum-consensus/src/primitives.rs: -------------------------------------------------------------------------------- 1 | use crate::ssz::prelude::*; 2 | pub use crate::{ 3 | crypto::{PublicKey as BlsPublicKey, Signature as BlsSignature}, 4 | domains::DomainType, 5 | ssz::prelude::U256, 6 | }; 7 | 8 | pub type Root = Node; 9 | pub type Slot = u64; 10 | pub type Epoch = u64; 11 | 12 | pub type CommitteeIndex = usize; 13 | pub type ValidatorIndex = usize; 14 | pub type WithdrawalIndex = usize; 15 | pub type BlobIndex = usize; 16 | pub type Gwei = u64; 17 | pub type Hash32 = Bytes32; 18 | 19 | pub type Version = [u8; 4]; 20 | pub type ForkDigest = [u8; 4]; 21 | pub type Domain = [u8; 32]; 22 | 23 | pub type ExecutionAddress = ByteVector<20>; 24 | 25 | pub type ChainId = usize; 26 | pub type NetworkId = usize; 27 | 28 | pub type RandaoReveal = BlsSignature; 29 | pub type Bytes32 = ByteVector<32>; 30 | 31 | pub type ParticipationFlags = u8; 32 | 33 | pub type ShuffledIndices = Vec; 34 | 35 | // Coordinate refers to a unique location in the block tree 36 | #[derive(serde::Serialize, serde::Deserialize)] 37 | pub struct Coordinate { 38 | #[serde(with = "crate::serde::as_str")] 39 | slot: Slot, 40 | root: Root, 41 | } 42 | 43 | pub const GENESIS_SLOT: Slot = 0; 44 | pub const GENESIS_EPOCH: Epoch = 0; 45 | pub const FAR_FUTURE_EPOCH: Epoch = Epoch::MAX; 46 | 47 | pub const BLS_WITHDRAWAL_PREFIX: u8 = 0x00; 48 | pub const ETH1_ADDRESS_WITHDRAWAL_PREFIX: u8 = 0x01; 49 | pub const COMPOUNDING_WITHDRAWAL_PREFIX: u8 = 0x02; 50 | 51 | #[cfg(test)] 52 | #[cfg(feature = "serde")] 53 | mod tests { 54 | use super::*; 55 | 56 | #[test] 57 | fn test_serde() { 58 | let bytes = Bytes32::default(); 59 | let json = serde_json::to_string(&bytes).unwrap(); 60 | assert_eq!(json, "\"0x0000000000000000000000000000000000000000000000000000000000000000\""); 61 | let bytes_roundtrip: Bytes32 = serde_json::from_str(&json).unwrap(); 62 | assert_eq!(bytes, bytes_roundtrip); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ethereum-consensus/src/electra/operations.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | phase0::AttestationData, 3 | primitives::{BlsSignature, Epoch, ValidatorIndex}, 4 | ssz::prelude::*, 5 | }; 6 | 7 | #[derive( 8 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 9 | )] 10 | pub struct AttesterSlashing { 11 | pub attestation_1: IndexedAttestation, 12 | pub attestation_2: IndexedAttestation, 13 | } 14 | 15 | #[derive( 16 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 17 | )] 18 | pub struct IndexedAttestation { 19 | #[serde(with = "crate::serde::seq_of_str")] 20 | pub attesting_indices: List, 21 | pub data: AttestationData, 22 | pub signature: BlsSignature, 23 | } 24 | 25 | #[derive( 26 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 27 | )] 28 | pub struct Attestation { 29 | pub aggregation_bits: Bitlist, 30 | pub data: AttestationData, 31 | pub committee_bits: Bitvector, 32 | pub signature: BlsSignature, 33 | } 34 | 35 | #[derive( 36 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 37 | )] 38 | pub struct Consolidation { 39 | #[serde(with = "crate::serde::as_str")] 40 | pub source_index: ValidatorIndex, 41 | #[serde(with = "crate::serde::as_str")] 42 | pub target_index: ValidatorIndex, 43 | #[serde(with = "crate::serde::as_str")] 44 | pub epoch: Epoch, 45 | } 46 | 47 | #[derive( 48 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 49 | )] 50 | pub struct SignedConsolidation { 51 | pub message: Consolidation, 52 | pub signature: BlsSignature, 53 | } 54 | -------------------------------------------------------------------------------- /spec-tests/runners/shuffling.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | runners::{gen_exec, gen_match_for}, 3 | test_case::TestCase, 4 | test_utils::{load_yaml, Error}, 5 | }; 6 | use ethereum_consensus::primitives::Bytes32; 7 | use serde::Deserialize; 8 | 9 | #[derive(Debug, Deserialize)] 10 | struct ShufflingTestData { 11 | seed: Bytes32, 12 | count: usize, 13 | mapping: Vec, 14 | } 15 | 16 | fn load_test(test_case_path: &str) -> ShufflingTestData { 17 | let path = test_case_path.to_string() + "/mapping.yaml"; 18 | load_yaml(&path) 19 | } 20 | 21 | pub fn dispatch(test: &TestCase) -> Result<(), Error> { 22 | match test.meta.handler.0.as_str() { 23 | "core" => { 24 | gen_match_for! { 25 | test, 26 | (mainnet, phase0), 27 | (minimal, phase0) 28 | { 29 | gen_exec! { 30 | test, 31 | load_test, 32 | |data: ShufflingTestData, context| { 33 | // test `compute_shuffled_index`, following the spec which goes index by index 34 | let result = (0..data.count).map(|index| { 35 | spec::compute_shuffled_index(index, data.count, &data.seed, context).unwrap() 36 | }).collect::>(); 37 | assert_eq!(result, data.mapping); 38 | 39 | // test `compute_shuffled_indices`, an optimization that shuffles the entire list at once 40 | let indices = (0..data.count).collect::>(); 41 | let shuffled_indices = spec::compute_shuffled_indices(&indices, &data.seed, context); 42 | assert_eq!(shuffled_indices, data.mapping); 43 | Ok(()) 44 | } 45 | } 46 | } 47 | } 48 | } 49 | handler => unreachable!("no tests for {handler}"), 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /spec-tests/runners/sanity.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | runners::{ 3 | gen_exec, gen_match_for_all, 4 | utils::{load_blocks_test, run_blocks_test}, 5 | }, 6 | test_case::TestCase, 7 | test_utils::{load_snappy_ssz, load_yaml, Error}, 8 | }; 9 | use ethereum_consensus::state_transition::Validation; 10 | 11 | fn load_test(test_case_path: &str) -> (S, S, u64) { 12 | let path = test_case_path.to_string() + "/pre.ssz_snappy"; 13 | let pre: S = load_snappy_ssz(&path).unwrap(); 14 | 15 | let path = test_case_path.to_string() + "/post.ssz_snappy"; 16 | let post: S = load_snappy_ssz(&path).unwrap(); 17 | 18 | let path = test_case_path.to_string() + "/slots.yaml"; 19 | let slots: u64 = load_yaml(&path); 20 | 21 | (pre, post, slots) 22 | } 23 | 24 | pub fn dispatch(test: &TestCase) -> Result<(), Error> { 25 | match test.meta.handler.0.as_str() { 26 | "blocks" => { 27 | gen_match_for_all! { 28 | test, 29 | load_blocks_test, 30 | |(pre, post, blocks): (spec::BeaconState, Option, Vec), context| { 31 | run_blocks_test(pre, post, blocks, context, |state, signed_block, context| { spec::state_transition(state, signed_block, Validation::Enabled, context) }) 32 | } 33 | } 34 | } 35 | "slots" => { 36 | gen_match_for_all! { 37 | test, 38 | load_test, 39 | |(mut pre, post, slots): (spec::BeaconState, spec::BeaconState, u64), context| { 40 | let target_slot = pre.slot + slots; 41 | spec::process_slots(&mut pre, target_slot, context)?; 42 | if pre != post { 43 | Err(Error::InvalidState) 44 | } else { 45 | Ok(()) 46 | } 47 | } 48 | } 49 | } 50 | handler => unreachable!("no tests for {handler}"), 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/blobs/command.rs: -------------------------------------------------------------------------------- 1 | use crate::blobs::{bundler, decode, encode, framing::Mode as Framing}; 2 | use clap::{Args, Subcommand, ValueEnum}; 3 | use std::io; 4 | 5 | #[derive(Debug, Subcommand)] 6 | enum Commands { 7 | Encode { 8 | #[arg(value_enum, default_value_t)] 9 | framing: FramingArg, 10 | }, 11 | Decode { 12 | #[arg(value_enum, default_value_t)] 13 | framing: FramingArg, 14 | }, 15 | Bundle, 16 | } 17 | 18 | #[derive(Debug, Clone, Default, ValueEnum)] 19 | enum FramingArg { 20 | Raw, 21 | #[default] 22 | Sized, 23 | } 24 | 25 | impl From for Framing { 26 | fn from(value: FramingArg) -> Self { 27 | match value { 28 | FramingArg::Raw => Framing::Raw, 29 | FramingArg::Sized => Framing::Sized, 30 | } 31 | } 32 | } 33 | 34 | #[derive(Debug, Args)] 35 | #[clap(about = "utilities for blobspace")] 36 | pub struct Command { 37 | #[clap(subcommand)] 38 | command: Commands, 39 | } 40 | 41 | impl Command { 42 | pub fn execute(self) -> eyre::Result<()> { 43 | match self.command { 44 | Commands::Encode { framing } => { 45 | let stdin = io::stdin().lock(); 46 | let blobs = encode::from_reader(stdin, framing.into())?; 47 | let result = serde_json::to_string_pretty(&blobs)?; 48 | println!("{}", result); 49 | Ok(()) 50 | } 51 | Commands::Decode { framing } => { 52 | let stdin = io::stdin().lock(); 53 | let stdout = io::stdout().lock(); 54 | decode::to_writer_from_json(stdin, stdout, framing.into())?; 55 | Ok(()) 56 | } 57 | Commands::Bundle => { 58 | let stdin = io::stdin().lock(); 59 | let blobs_bundle = bundler::from_reader(stdin)?; 60 | let result = serde_json::to_string_pretty(&blobs_bundle)?; 61 | println!("{}", result); 62 | Ok(()) 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /spec-gen/README.md: -------------------------------------------------------------------------------- 1 | # `spec-gen` 2 | 3 | Generates Rust source of each fork of the `consensus-specs` based on prior forks 4 | and the patches applied for each target fork. 5 | 6 | ## How to use 7 | 8 | From the workspace root: 9 | 10 | ```bash 11 | just gen-spec 12 | ``` 13 | 14 | ## Structure 15 | 16 | Each fork has the following structure: 17 | 18 | A "fork diff" that implements the new logic to be applied on top of the previous fork, written as (private) modules in the top-level under the forks' top-level module. For example, `block_processing` in `phase0`. 19 | 20 | Modules to add to a fork diff as discovered by adding them to the data returned from the `Fork::modules` function defined in this tool. 21 | 22 | A "fork diff" is applied, on top of any previous specs to generate the target spec which is written as a single file under the fork's `spec` module. For example `phase0::spec` under `phase0/spec/mod.rs`. The choice of the nested `mod.rs` file is arbitrary and mainly used to obscure the generated file (if done properly, the user shouldn't really need to think about it). 23 | 24 | Further, each fork has "presets", like `mainnet`, which fix all of the type generics with the appropriate values defined in the preset. This preset pulls in everything defined in the `spec` module and then specializes the relevant types so that a user can simply pull from a preset and not have to bother with any generic types. 25 | 26 | In general, an author of a new fork should only expose the presets, generated spec and any other modules not touched the spec generator tool. 27 | 28 | ## Steps to add a new fork 29 | 30 | Write the `BeaconState` and `BeaconBlock` types. These tend to change from fork to fork and will almost certainly touch many of the supporting functions from earlier specs. 31 | 32 | Run the generator. This should write a "partial" spec in the `spec` module of the new fork and any functionality from a previous fork should be found here, ready to be used with the new fork's types (e.g. the new `BeaconState`). 33 | 34 | The spec generator should be run a final time once all of the new fork's functionality is in place. 35 | -------------------------------------------------------------------------------- /ethereum-consensus/src/ssz/byte_list.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | serde::{write_bytes_to_lower_hex, write_bytes_to_lower_hex_display}, 3 | ssz::prelude::*, 4 | }; 5 | use std::{ 6 | fmt, 7 | hash::{Hash, Hasher}, 8 | ops::{Deref, DerefMut}, 9 | }; 10 | 11 | #[derive(Default, Clone, PartialEq, Eq, SimpleSerialize, serde::Serialize, serde::Deserialize)] 12 | pub struct ByteList(#[serde(with = "crate::serde::as_hex")] List); 13 | 14 | impl TryFrom<&[u8]> for ByteList { 15 | type Error = DeserializeError; 16 | 17 | fn try_from(bytes: &[u8]) -> Result { 18 | ByteList::::deserialize(bytes) 19 | } 20 | } 21 | 22 | impl Hash for ByteList { 23 | fn hash(&self, state: &mut H) { 24 | self.as_ref().hash(state); 25 | } 26 | } 27 | 28 | impl fmt::Debug for ByteList { 29 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 30 | write_bytes_to_lower_hex(f, &self.0) 31 | } 32 | } 33 | 34 | impl fmt::Display for ByteList { 35 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 36 | write_bytes_to_lower_hex_display(f, self.0.iter()) 37 | } 38 | } 39 | 40 | impl AsRef<[u8]> for ByteList { 41 | fn as_ref(&self) -> &[u8] { 42 | self.0.as_ref() 43 | } 44 | } 45 | 46 | impl Deref for ByteList { 47 | type Target = List; 48 | 49 | fn deref(&self) -> &Self::Target { 50 | &self.0 51 | } 52 | } 53 | 54 | impl DerefMut for ByteList { 55 | fn deref_mut(&mut self) -> &mut Self::Target { 56 | &mut self.0 57 | } 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use super::*; 63 | 64 | #[test] 65 | fn test_byte_list_serde() { 66 | let list = ByteList::<32>::try_from([255u8, 255u8].as_ref()).unwrap(); 67 | let encoding = serialize(&list).unwrap(); 68 | assert_eq!(encoding, [255, 255]); 69 | 70 | let recovered_list = ByteList::<32>::deserialize(&encoding).unwrap(); 71 | assert_eq!(list, recovered_list); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /ethereum-consensus/examples/serde.rs: -------------------------------------------------------------------------------- 1 | #![cfg(feature = "serde")] 2 | 3 | use ethereum_consensus::{ 4 | altair::mainnet::SyncCommittee, 5 | bellatrix::mainnet::ExecutionPayloadHeader, 6 | phase0::mainnet::{ 7 | Attestation, AttesterSlashing, BeaconState, Deposit, PendingAttestation, ProposerSlashing, 8 | SignedBeaconBlock, SignedVoluntaryExit, 9 | }, 10 | types::mainnet as types, 11 | }; 12 | 13 | fn main() { 14 | let mut block = SignedBeaconBlock::default(); 15 | block.message.body.proposer_slashings.push(ProposerSlashing::default()); 16 | block.message.body.attester_slashings.push(AttesterSlashing::default()); 17 | block.message.body.attestations.push(Attestation::default()); 18 | block.message.body.deposits.push(Deposit::default()); 19 | block.message.body.voluntary_exits.push(SignedVoluntaryExit::default()); 20 | 21 | let block_json = serde_json::to_string(&block).unwrap(); 22 | println!("{block_json}"); 23 | let _: SignedBeaconBlock = serde_json::from_str(&block_json).unwrap(); 24 | 25 | let mut state = BeaconState::default(); 26 | state.current_epoch_attestations.push(PendingAttestation::default()); 27 | 28 | let state_json = serde_json::to_string(&state).unwrap(); 29 | println!("{state_json}"); 30 | let _: BeaconState = serde_json::from_str(&state_json).unwrap(); 31 | 32 | let sync_committee = SyncCommittee::default(); 33 | let sync_committee_json = serde_json::to_string(&sync_committee).unwrap(); 34 | println!("{}", &sync_committee_json); 35 | let _: SyncCommittee = serde_json::from_str(&sync_committee_json).unwrap(); 36 | 37 | let header = ExecutionPayloadHeader::default(); 38 | let header_json = serde_json::to_string(&header).unwrap(); 39 | println!("{}", &header_json); 40 | let _: ExecutionPayloadHeader = serde_json::from_str(&header_json).unwrap(); 41 | 42 | let header = types::ExecutionPayloadHeader::Bellatrix(header); 43 | let header_json = serde_json::to_string_pretty(&header).unwrap(); 44 | println!("{header_json}"); 45 | let recovered_header = serde_json::from_str(&header_json).unwrap(); 46 | assert_eq!(header, recovered_header); 47 | } 48 | -------------------------------------------------------------------------------- /ethereum-consensus/src/configs/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod goerli; 2 | pub mod holesky; 3 | pub mod hoodi; 4 | pub mod mainnet; 5 | pub mod minimal; 6 | pub mod sepolia; 7 | 8 | use crate::{ 9 | networks::Network, 10 | primitives::{Epoch, ExecutionAddress, Gwei, Hash32, Version, U256}, 11 | }; 12 | 13 | #[derive(Debug, serde::Deserialize)] 14 | #[serde(rename_all = "UPPERCASE")] 15 | pub struct Config { 16 | pub preset_base: String, 17 | #[serde(rename = "CONFIG_NAME")] 18 | pub name: Network, 19 | 20 | pub terminal_total_difficulty: U256, 21 | pub terminal_block_hash: Hash32, 22 | pub terminal_block_hash_activation_epoch: Epoch, 23 | 24 | pub min_genesis_active_validator_count: usize, 25 | pub min_genesis_time: u64, 26 | #[serde(with = "crate::serde::as_hex")] 27 | pub genesis_fork_version: Version, 28 | pub genesis_delay: u64, 29 | 30 | #[serde(with = "crate::serde::as_hex")] 31 | pub altair_fork_version: Version, 32 | pub altair_fork_epoch: Epoch, 33 | #[serde(with = "crate::serde::as_hex")] 34 | pub bellatrix_fork_version: Version, 35 | pub bellatrix_fork_epoch: Epoch, 36 | #[serde(with = "crate::serde::as_hex")] 37 | pub capella_fork_version: Version, 38 | pub capella_fork_epoch: Epoch, 39 | #[serde(with = "crate::serde::as_hex")] 40 | pub deneb_fork_version: Version, 41 | pub deneb_fork_epoch: Epoch, 42 | #[serde(with = "crate::serde::as_hex")] 43 | pub electra_fork_version: Version, 44 | pub electra_fork_epoch: Epoch, 45 | 46 | pub seconds_per_slot: u64, 47 | pub seconds_per_eth1_block: u64, 48 | pub min_validator_withdrawability_delay: Epoch, 49 | pub shard_committee_period: Epoch, 50 | pub eth1_follow_distance: u64, 51 | 52 | pub inactivity_score_bias: u64, 53 | pub inactivity_score_recovery_rate: u64, 54 | pub ejection_balance: Gwei, 55 | pub min_per_epoch_churn_limit: u64, 56 | pub max_per_epoch_activation_churn_limit: u64, 57 | pub churn_limit_quotient: u64, 58 | pub min_per_epoch_churn_limit_electra: u64, 59 | pub max_per_epoch_activation_exit_churn_limit: u64, 60 | 61 | pub proposer_score_boost: u64, 62 | 63 | pub deposit_chain_id: usize, 64 | pub deposit_network_id: usize, 65 | pub deposit_contract_address: ExecutionAddress, 66 | } 67 | -------------------------------------------------------------------------------- /spec-tests/test_case.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | runners::{ 3 | bls, epoch_processing, finality, fork, genesis, kzg, light_client, merkle_proof, 4 | operations, random, rewards, sanity, shuffling, ssz_static, transition, 5 | }, 6 | test_meta::TestMeta, 7 | Config, Context, 8 | Runner::*, 9 | }; 10 | use ethereum_consensus::state_transition; 11 | use std::{error::Error, path::Path, sync::Arc}; 12 | 13 | pub struct TestCase { 14 | pub meta: TestMeta, 15 | pub data_path: String, 16 | pub context: Arc, 17 | } 18 | 19 | impl TestCase { 20 | pub fn new(meta: TestMeta, data_path: &Path, context: Arc) -> Self { 21 | Self { meta, data_path: data_path.as_os_str().to_str().unwrap().into(), context } 22 | } 23 | 24 | pub fn name(&self) -> String { 25 | self.meta.name() 26 | } 27 | 28 | pub fn context(&self) -> &state_transition::Context { 29 | match self.meta.config { 30 | Config::Mainnet => &self.context.mainnet, 31 | Config::Minimal => &self.context.minimal, 32 | Config::General => &self.context.mainnet, 33 | } 34 | } 35 | 36 | pub fn execute(&self) -> Result<(), Box> { 37 | let result = match &self.meta.runner { 38 | Bls => bls::dispatch(self), 39 | EpochProcessing => epoch_processing::dispatch(self), 40 | Finality => finality::dispatch(self), 41 | ForkChoice => todo!(), 42 | Fork => fork::dispatch(self), 43 | Genesis => genesis::dispatch(self), 44 | Operations => operations::dispatch(self), 45 | Random => random::dispatch(self), 46 | Rewards => rewards::dispatch(self), 47 | Sanity => sanity::dispatch(self), 48 | Shuffling => shuffling::dispatch(self), 49 | SszStatic => ssz_static::dispatch(self), 50 | Transition => transition::dispatch(self), 51 | LightClient => light_client::dispatch(self), 52 | Kzg => kzg::dispatch(self), 53 | MerkleProof => merkle_proof::dispatch(self), 54 | Sync => todo!(), 55 | SszGeneric => unreachable!(), 56 | }; 57 | match result { 58 | Err(crate::test_utils::Error::InternalContinue) => { 59 | panic!("invariant violated; this error should not be surfaced to user") 60 | } 61 | other => other.map_err(Into::into), 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/light_client.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::{ 3 | sync::{SyncAggregate, SyncCommittee}, 4 | BeaconBlockHeader, 5 | }, 6 | primitives::{Bytes32, Slot}, 7 | ssz::prelude::*, 8 | }; 9 | 10 | pub const FINALIZED_ROOT_INDEX: usize = 105; 11 | pub const FINALIZED_ROOT_INDEX_FLOOR_LOG_2: usize = 6; 12 | 13 | pub const CURRENT_SYNC_COMMITTEE_INDEX: usize = 54; 14 | pub const CURRENT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2: usize = 5; 15 | 16 | pub const NEXT_SYNC_COMMITTEE_INDEX: usize = 55; 17 | pub const NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2: usize = 5; 18 | 19 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 20 | pub struct LightClientHeader { 21 | pub beacon: BeaconBlockHeader, 22 | } 23 | 24 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 25 | pub struct LightClientBootstrap { 26 | pub header: LightClientHeader, 27 | pub current_sync_committee: SyncCommittee, 28 | pub current_sync_committee_branch: Vector, 29 | } 30 | 31 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 32 | pub struct LightClientUpdate { 33 | pub attested_header: LightClientHeader, 34 | pub next_sync_committee: SyncCommittee, 35 | pub next_sync_committee_branch: Vector, 36 | pub finalized_header: LightClientHeader, 37 | pub finality_branch: Vector, 38 | pub sync_aggregate: SyncAggregate, 39 | pub signature_slot: Slot, 40 | } 41 | 42 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 43 | pub struct LightClientFinalityUpdate { 44 | pub attested_header: LightClientHeader, 45 | pub finalized_header: LightClientHeader, 46 | pub finality_branch: Vector, 47 | pub sync_aggregate: SyncAggregate, 48 | pub signature_slot: Slot, 49 | } 50 | 51 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 52 | pub struct LightClientOptimisticUpdate { 53 | pub attested_header: LightClientHeader, 54 | pub sync_aggregate: SyncAggregate, 55 | pub signature_slot: Slot, 56 | } 57 | -------------------------------------------------------------------------------- /ethereum-consensus/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ethereum-consensus" 3 | version = "0.1.1" 4 | edition = "2021" 5 | license = "MIT OR Apache-2.0" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | [features] 9 | default = ["serde", "async"] 10 | serde = ["hex", "serde_json", "serde_yaml"] 11 | async = ["tokio", "tokio-stream"] 12 | optimized = ["shuffling"] 13 | shuffling = [] # supports optimized shuffling routines 14 | secret-key-debug = [ 15 | ] # enable if you want to be able to print `crypto::SecretKey` 16 | spec-tests = [] # enable extra features for testing 17 | ec = [ 18 | "secret-key-debug", 19 | "clap", 20 | "eyre", 21 | "bip39", 22 | "rand_core", 23 | "rayon", 24 | "hkdf", 25 | "ruint", 26 | "uuid", 27 | "scrypt", 28 | "serde", 29 | "aes", 30 | "ctr", 31 | "base64", 32 | "unicode-normalization", 33 | "bitvec", 34 | ] 35 | 36 | [dependencies] 37 | ssz_rs = { workspace = true } 38 | blst = { workspace = true } 39 | rand = { workspace = true } 40 | thiserror = { workspace = true } 41 | sha2 = { workspace = true } 42 | integer-sqrt = { workspace = true } 43 | enr = { workspace = true } 44 | multihash = { workspace = true } 45 | multiaddr = { workspace = true } 46 | c-kzg = { workspace = true } 47 | serde = { workspace = true } 48 | serde_json = { workspace = true, optional = true } 49 | serde_yaml = { workspace = true, optional = true } 50 | hex = { workspace = true, optional = true } 51 | tokio = { workspace = true, optional = true } 52 | tokio-stream = { workspace = true, optional = true } 53 | bs58 = { workspace = true } 54 | clap = { workspace = true, optional = true } 55 | eyre = { workspace = true, optional = true } 56 | bip39 = { workspace = true, optional = true } 57 | rand_core = { workspace = true, optional = true } 58 | rayon = { workspace = true, optional = true } 59 | hkdf = { workspace = true, optional = true } 60 | ruint = { workspace = true, optional = true } 61 | uuid = { workspace = true, optional = true } 62 | scrypt = { workspace = true, optional = true } 63 | aes = { workspace = true, optional = true } 64 | ctr = { workspace = true, optional = true } 65 | base64 = { workspace = true, optional = true } 66 | unicode-normalization = { workspace = true, optional = true } 67 | bitvec = { workspace = true, optional = true } 68 | 69 | [dev-dependencies] 70 | alloy-eips = { version = "0.13.0", default-features = false} 71 | reth-ethereum-primitives = { git = "https://github.com/paradigmxyz/reth", tag = "v1.3.7", default-features = false } 72 | 73 | 74 | [[bin]] 75 | name = "ec" 76 | required-features = ["ec"] 77 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/beacon_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::{ 3 | sync::SyncCommittee, BeaconBlockHeader, Checkpoint, Eth1Data, Fork, Validator, 4 | JUSTIFICATION_BITS_LENGTH, 5 | }, 6 | primitives::{Bytes32, Gwei, ParticipationFlags, Root, Slot}, 7 | ssz::prelude::*, 8 | }; 9 | 10 | #[derive( 11 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 12 | )] 13 | pub struct BeaconState< 14 | const SLOTS_PER_HISTORICAL_ROOT: usize, 15 | const HISTORICAL_ROOTS_LIMIT: usize, 16 | const ETH1_DATA_VOTES_BOUND: usize, 17 | const VALIDATOR_REGISTRY_LIMIT: usize, 18 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 19 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 20 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 21 | const SYNC_COMMITTEE_SIZE: usize, 22 | > { 23 | #[serde(with = "crate::serde::as_str")] 24 | pub genesis_time: u64, 25 | pub genesis_validators_root: Root, 26 | #[serde(with = "crate::serde::as_str")] 27 | pub slot: Slot, 28 | pub fork: Fork, 29 | pub latest_block_header: BeaconBlockHeader, 30 | pub block_roots: Vector, 31 | pub state_roots: Vector, 32 | pub historical_roots: List, 33 | pub eth1_data: Eth1Data, 34 | pub eth1_data_votes: List, 35 | #[serde(with = "crate::serde::as_str")] 36 | pub eth1_deposit_index: u64, 37 | pub validators: List, 38 | #[serde(with = "crate::serde::seq_of_str")] 39 | pub balances: List, 40 | pub randao_mixes: Vector, 41 | #[serde(with = "crate::serde::seq_of_str")] 42 | pub slashings: Vector, 43 | #[serde(with = "crate::serde::seq_of_str")] 44 | pub previous_epoch_participation: List, 45 | #[serde(with = "crate::serde::seq_of_str")] 46 | pub current_epoch_participation: List, 47 | pub justification_bits: Bitvector, 48 | pub previous_justified_checkpoint: Checkpoint, 49 | pub current_justified_checkpoint: Checkpoint, 50 | pub finalized_checkpoint: Checkpoint, 51 | #[serde(with = "crate::serde::seq_of_str")] 52 | pub inactivity_scores: List, 53 | pub current_sync_committee: SyncCommittee, 54 | pub next_sync_committee: SyncCommittee, 55 | } 56 | -------------------------------------------------------------------------------- /spec-tests/runners/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bls; 2 | pub mod epoch_processing; 3 | pub mod finality; 4 | pub mod fork; 5 | pub mod genesis; 6 | pub mod kzg; 7 | pub mod light_client; 8 | pub mod merkle_proof; 9 | pub mod operations; 10 | pub mod random; 11 | pub mod rewards; 12 | pub mod sanity; 13 | pub mod shuffling; 14 | pub mod ssz_static; 15 | pub mod transition; 16 | pub mod utils; 17 | 18 | macro_rules! gen_exec { 19 | ($test_case:expr, $loader_fn:expr, $exec_fn:expr) => { 20 | let inputs = $loader_fn(&$test_case.data_path); 21 | let context = $test_case.context(); 22 | $exec_fn(inputs, &context) 23 | }; 24 | } 25 | 26 | macro_rules! gen_match_for { 27 | ($test_case:expr, $(($target_config:ident, $target_fork:ident)),+ $target_body:block) => { 28 | match ($test_case.meta.config, $test_case.meta.fork) { 29 | $( 30 | paste::paste! { (crate::test_meta::Config::[<$target_config:camel>], crate::test_meta::Fork::[<$target_fork:camel>]) } => { 31 | paste::paste! { use ethereum_consensus::[<$target_fork>]::[<$target_config>] as spec; } 32 | $target_body 33 | } 34 | )+ 35 | pair => unreachable!("no tests for {pair:?}"), 36 | } 37 | }; 38 | ($test_case:expr, $(($target_config:ident, $target_fork:ident) => $target_body:block)+) => { 39 | match ($test_case.meta.config, $test_case.meta.fork) { 40 | $( 41 | paste::paste! { (crate::test_meta::Config::[<$target_config:camel>], crate::test_meta::Fork::[<$target_fork:camel>]) } => { 42 | paste::paste! { use ethereum_consensus::[<$target_fork>]::[<$target_config>] as spec; } 43 | $target_body 44 | } 45 | )+ 46 | pair => unreachable!("no tests for {pair:?}"), 47 | } 48 | }; 49 | } 50 | 51 | macro_rules! gen_match_for_all { 52 | ($test_case:expr, $loader_fn:expr, $exec_fn:expr) => { 53 | crate::runners::gen_match_for! { 54 | $test_case, 55 | (mainnet, phase0), 56 | (mainnet, altair), 57 | (mainnet, bellatrix), 58 | (mainnet, capella), 59 | (mainnet, deneb), 60 | (minimal, phase0), 61 | (minimal, altair), 62 | (minimal, bellatrix), 63 | (minimal, capella), 64 | (minimal, deneb) 65 | { 66 | gen_exec! { $test_case, $loader_fn, $exec_fn } 67 | } 68 | } 69 | }; 70 | } 71 | 72 | pub(crate) use gen_exec; 73 | pub(crate) use gen_match_for; 74 | pub(crate) use gen_match_for_all; 75 | -------------------------------------------------------------------------------- /ethereum-consensus/src/networks.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | /// This module contains support for various Ethereum netowrks. 4 | use crate::state_transition::Context; 5 | use crate::Error; 6 | 7 | /// `Network` describes one of the established networks this repository supports 8 | /// or otherwise a `Custom` variant that wraps a path to a local configuration directory 9 | /// for the custom network (useful for devnets). 10 | #[derive(Default, Debug, Clone, PartialEq, Eq, serde::Deserialize, serde::Serialize)] 11 | #[serde(rename_all = "lowercase", into = "String", from = "String")] 12 | pub enum Network { 13 | #[default] 14 | Mainnet, 15 | Sepolia, 16 | Goerli, 17 | Holesky, 18 | Hoodi, 19 | Custom(String), 20 | } 21 | 22 | impl std::fmt::Display for Network { 23 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 24 | match self { 25 | Self::Mainnet => write!(f, "mainnet"), 26 | Self::Sepolia => write!(f, "sepolia"), 27 | Self::Goerli => write!(f, "goerli"), 28 | Self::Holesky => write!(f, "holesky"), 29 | Self::Hoodi => write!(f, "hoodi"), 30 | Self::Custom(config_dir) => write!(f, "custom ({config_dir}/config.yaml)"), 31 | } 32 | } 33 | } 34 | 35 | impl From for String { 36 | fn from(value: Network) -> Self { 37 | format!("{value}") 38 | } 39 | } 40 | 41 | impl From for Network { 42 | fn from(value: String) -> Self { 43 | match value.as_str() { 44 | "mainnet" => Self::Mainnet, 45 | "sepolia" => Self::Sepolia, 46 | "goerli" => Self::Goerli, 47 | "holesky" => Self::Holesky, 48 | "hoodi" => Self::Hoodi, 49 | _ => Self::Custom(value), 50 | } 51 | } 52 | } 53 | 54 | impl TryFrom for Context { 55 | type Error = Error; 56 | 57 | fn try_from(network: Network) -> Result { 58 | match network { 59 | Network::Mainnet => Ok(Context::for_mainnet()), 60 | Network::Sepolia => Ok(Context::for_sepolia()), 61 | Network::Goerli => Ok(Context::for_goerli()), 62 | Network::Holesky => Ok(Context::for_holesky()), 63 | Network::Hoodi => Ok(Context::for_hoodi()), 64 | Network::Custom(config) => { 65 | let config_file = PathBuf::from(config).join("config.yaml"); 66 | Context::try_from_file(config_file) 67 | } 68 | } 69 | } 70 | } 71 | 72 | // NOTE: the default genesis time here is usually seen on testnets 73 | // where we have control over the genesis details 74 | pub fn typical_genesis_time(context: &Context) -> u64 { 75 | context.min_genesis_time + context.genesis_delay 76 | } 77 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bellatrix/beacon_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bellatrix::{ 3 | BeaconBlockHeader, Checkpoint, Eth1Data, ExecutionPayloadHeader, Fork, SyncCommittee, 4 | Validator, JUSTIFICATION_BITS_LENGTH, 5 | }, 6 | primitives::{Bytes32, Gwei, ParticipationFlags, Root, Slot}, 7 | ssz::prelude::*, 8 | }; 9 | 10 | #[derive( 11 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 12 | )] 13 | pub struct BeaconState< 14 | const SLOTS_PER_HISTORICAL_ROOT: usize, 15 | const HISTORICAL_ROOTS_LIMIT: usize, 16 | const ETH1_DATA_VOTES_BOUND: usize, 17 | const VALIDATOR_REGISTRY_LIMIT: usize, 18 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 19 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 20 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 21 | const SYNC_COMMITTEE_SIZE: usize, 22 | const BYTES_PER_LOGS_BLOOM: usize, 23 | const MAX_EXTRA_DATA_BYTES: usize, 24 | > { 25 | #[serde(with = "crate::serde::as_str")] 26 | pub genesis_time: u64, 27 | pub genesis_validators_root: Root, 28 | #[serde(with = "crate::serde::as_str")] 29 | pub slot: Slot, 30 | pub fork: Fork, 31 | pub latest_block_header: BeaconBlockHeader, 32 | pub block_roots: Vector, 33 | pub state_roots: Vector, 34 | pub historical_roots: List, 35 | pub eth1_data: Eth1Data, 36 | pub eth1_data_votes: List, 37 | #[serde(with = "crate::serde::as_str")] 38 | pub eth1_deposit_index: u64, 39 | pub validators: List, 40 | #[serde(with = "crate::serde::seq_of_str")] 41 | pub balances: List, 42 | pub randao_mixes: Vector, 43 | #[serde(with = "crate::serde::seq_of_str")] 44 | pub slashings: Vector, 45 | #[serde(with = "crate::serde::seq_of_str")] 46 | pub previous_epoch_participation: List, 47 | #[serde(with = "crate::serde::seq_of_str")] 48 | pub current_epoch_participation: List, 49 | pub justification_bits: Bitvector, 50 | pub previous_justified_checkpoint: Checkpoint, 51 | pub current_justified_checkpoint: Checkpoint, 52 | pub finalized_checkpoint: Checkpoint, 53 | #[serde(with = "crate::serde::seq_of_str")] 54 | pub inactivity_scores: List, 55 | pub current_sync_committee: SyncCommittee, 56 | pub next_sync_committee: SyncCommittee, 57 | pub latest_execution_payload_header: 58 | ExecutionPayloadHeader, 59 | } 60 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/README.md: -------------------------------------------------------------------------------- 1 | # `ec` 2 | 3 | A set of utilities for Ethereum consensus. 4 | 5 | ## Blobs 6 | 7 | Facilities for mapping an arbitrary stream of data into blobs and back. 8 | 9 | To produce blobs from some data, do something like this: 10 | 11 | ```bash 12 | $ cat some-data | ec blobs encode > encoded-blobs.json 13 | ``` 14 | 15 | This produces a JSON array of complete blobs on STDOUT. 16 | The blobs are sequenced in the same order to match the incoming data. 17 | If you change the order of the blobs, the recovered data will not be the same. 18 | 19 | To bundle the blobs for including into an EIP-4844 transaction (including computing commitments and proofs): 20 | 21 | ```bash 22 | $ cat encoded-blobs.json | ec blobs bundle > blobs-bundle.json 23 | ``` 24 | 25 | The blobs, commitments, and proofs are required when making a valid 4844 transaction to submit to an execution node. 26 | This utility currently does not support making the 4844 transaction; refer to something like [alloy](https://github.com/alloy-rs/alloy) for this. 27 | 28 | To recover some data from a set of blobs (e.g. from the chain), assemble them into a JSON array and provide as input on STDIN: 29 | 30 | ```bash 31 | $ cat blobs.json | ec blobs decode > some-data 32 | ``` 33 | 34 | Note that the order of the blobs must be maintained as was first produced by `ec blobs encode` if one wishes to recover the same data stream as initially provided. 35 | 36 | ### Framing 37 | 38 | The blob command supports various framing modes to support placing arbitrary data into blobs and being able to recover it. 39 | 40 | Supported modes: 41 | 42 | * `raw` 43 | * `sized` 44 | 45 | The `raw` mode just writes whatever data is provided directly into blobs. Note that given the fixed size of blobs, this could mean padding bytes are added to the end of the stream and there is no way to know from the blob data where the original data ended. There is no (local) limit to the amount of data that can {en,de}coded to/from blobs this way. 46 | 47 | The `sized` mode adds a header to the payload data so that this utility can read exactly the originally written number of bytes when decoding. 48 | Refer to the documentation for details of the header and payload encoding. 49 | The `sized` mode gives no other sequencing data so users must take care when ordering blobs if the order is meaningful (e.g. when decoding). 50 | If the target data fits within the maximum number of blobs per block, then a user can simply use this tool (keeping blobs in the same order at each step) 51 | and use the Ethereum protocol nonce and blob index as sequencing data. If the target data exceeds the maximum number of blobs per block, the user will either need to manually place blobs such that 52 | the blob order respects the (nonce, blob index) order, or devise some other sequencing scheme (e.g. the payload data can include in-band sequencing information). 53 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/slot_processing.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | phase0::{beacon_state::BeaconState, epoch_processing::process_epoch}, 3 | primitives::{Root, Slot}, 4 | ssz::prelude::*, 5 | state_transition::{Context, Result}, 6 | Error, 7 | }; 8 | 9 | pub fn process_slots< 10 | const SLOTS_PER_HISTORICAL_ROOT: usize, 11 | const HISTORICAL_ROOTS_LIMIT: usize, 12 | const ETH1_DATA_VOTES_BOUND: usize, 13 | const VALIDATOR_REGISTRY_LIMIT: usize, 14 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 15 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 16 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 17 | const PENDING_ATTESTATIONS_BOUND: usize, 18 | >( 19 | state: &mut BeaconState< 20 | SLOTS_PER_HISTORICAL_ROOT, 21 | HISTORICAL_ROOTS_LIMIT, 22 | ETH1_DATA_VOTES_BOUND, 23 | VALIDATOR_REGISTRY_LIMIT, 24 | EPOCHS_PER_HISTORICAL_VECTOR, 25 | EPOCHS_PER_SLASHINGS_VECTOR, 26 | MAX_VALIDATORS_PER_COMMITTEE, 27 | PENDING_ATTESTATIONS_BOUND, 28 | >, 29 | slot: Slot, 30 | context: &Context, 31 | ) -> Result<()> { 32 | if state.slot >= slot { 33 | return Err(Error::TransitionToPreviousSlot { requested: slot, current: state.slot }) 34 | } 35 | while state.slot < slot { 36 | process_slot(state, context)?; 37 | if (state.slot + 1) % context.slots_per_epoch == 0 { 38 | process_epoch(state, context)?; 39 | } 40 | state.slot += 1; 41 | } 42 | Ok(()) 43 | } 44 | 45 | pub fn process_slot< 46 | const SLOTS_PER_HISTORICAL_ROOT: usize, 47 | const HISTORICAL_ROOTS_LIMIT: usize, 48 | const ETH1_DATA_VOTES_BOUND: usize, 49 | const VALIDATOR_REGISTRY_LIMIT: usize, 50 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 51 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 52 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 53 | const PENDING_ATTESTATIONS_BOUND: usize, 54 | >( 55 | state: &mut BeaconState< 56 | SLOTS_PER_HISTORICAL_ROOT, 57 | HISTORICAL_ROOTS_LIMIT, 58 | ETH1_DATA_VOTES_BOUND, 59 | VALIDATOR_REGISTRY_LIMIT, 60 | EPOCHS_PER_HISTORICAL_VECTOR, 61 | EPOCHS_PER_SLASHINGS_VECTOR, 62 | MAX_VALIDATORS_PER_COMMITTEE, 63 | PENDING_ATTESTATIONS_BOUND, 64 | >, 65 | context: &Context, 66 | ) -> Result<()> { 67 | let previous_state_root = state.hash_tree_root()?; 68 | let root_index = state.slot % context.slots_per_historical_root; 69 | state.state_roots[root_index as usize] = previous_state_root; 70 | 71 | if state.latest_block_header.state_root == Root::default() { 72 | state.latest_block_header.state_root = previous_state_root; 73 | } 74 | 75 | let previous_block_root = state.latest_block_header.hash_tree_root()?; 76 | let root_index = state.slot % context.slots_per_historical_root; 77 | state.block_roots[root_index as usize] = previous_block_root; 78 | 79 | Ok(()) 80 | } 81 | -------------------------------------------------------------------------------- /ethereum-consensus/src/deneb/epoch_processing.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | deneb::{ 3 | compute_activation_exit_epoch, get_current_epoch, get_validator_activation_churn_limit, 4 | initiate_validator_exit, is_active_validator, is_eligible_for_activation, 5 | is_eligible_for_activation_queue, BeaconState, 6 | }, 7 | primitives::ValidatorIndex, 8 | state_transition::{Context, Result}, 9 | }; 10 | 11 | pub fn process_registry_updates< 12 | const SLOTS_PER_HISTORICAL_ROOT: usize, 13 | const HISTORICAL_ROOTS_LIMIT: usize, 14 | const ETH1_DATA_VOTES_BOUND: usize, 15 | const VALIDATOR_REGISTRY_LIMIT: usize, 16 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 17 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 18 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 19 | const SYNC_COMMITTEE_SIZE: usize, 20 | const BYTES_PER_LOGS_BLOOM: usize, 21 | const MAX_EXTRA_DATA_BYTES: usize, 22 | >( 23 | state: &mut BeaconState< 24 | SLOTS_PER_HISTORICAL_ROOT, 25 | HISTORICAL_ROOTS_LIMIT, 26 | ETH1_DATA_VOTES_BOUND, 27 | VALIDATOR_REGISTRY_LIMIT, 28 | EPOCHS_PER_HISTORICAL_VECTOR, 29 | EPOCHS_PER_SLASHINGS_VECTOR, 30 | MAX_VALIDATORS_PER_COMMITTEE, 31 | SYNC_COMMITTEE_SIZE, 32 | BYTES_PER_LOGS_BLOOM, 33 | MAX_EXTRA_DATA_BYTES, 34 | >, 35 | context: &Context, 36 | ) -> Result<()> { 37 | let current_epoch = get_current_epoch(state, context); 38 | for i in 0..state.validators.len() { 39 | let validator = &mut state.validators[i]; 40 | if is_eligible_for_activation_queue(validator, context) { 41 | validator.activation_eligibility_epoch = current_epoch + 1; 42 | } 43 | if is_active_validator(validator, current_epoch) && 44 | validator.effective_balance <= context.ejection_balance 45 | { 46 | initiate_validator_exit(state, i, context)?; 47 | } 48 | } 49 | let mut activation_queue = 50 | state 51 | .validators 52 | .iter() 53 | .enumerate() 54 | .filter_map(|(index, validator)| { 55 | if is_eligible_for_activation(state, validator) { 56 | Some(index) 57 | } else { 58 | None 59 | } 60 | }) 61 | .collect::>(); 62 | activation_queue.sort_by(|&i, &j| { 63 | let a = &state.validators[i]; 64 | let b = &state.validators[j]; 65 | (a.activation_eligibility_epoch, i).cmp(&(b.activation_eligibility_epoch, j)) 66 | }); 67 | let activation_exit_epoch = compute_activation_exit_epoch(current_epoch, context); 68 | for i in activation_queue.into_iter().take(get_validator_activation_churn_limit(state, context)) 69 | { 70 | let validator = &mut state.validators[i]; 71 | validator.activation_epoch = activation_exit_epoch; 72 | } 73 | Ok(()) 74 | } 75 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/beacon_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::{ 3 | sync::SyncAggregate, Attestation, AttesterSlashing, Deposit, Eth1Data, ProposerSlashing, 4 | SignedVoluntaryExit, 5 | }, 6 | primitives::{BlsSignature, Bytes32, Root, Slot, ValidatorIndex}, 7 | ssz::prelude::*, 8 | }; 9 | 10 | #[derive( 11 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 12 | )] 13 | pub struct BeaconBlockBody< 14 | const MAX_PROPOSER_SLASHINGS: usize, 15 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 16 | const MAX_ATTESTER_SLASHINGS: usize, 17 | const MAX_ATTESTATIONS: usize, 18 | const MAX_DEPOSITS: usize, 19 | const MAX_VOLUNTARY_EXITS: usize, 20 | const SYNC_COMMITTEE_SIZE: usize, 21 | > { 22 | pub randao_reveal: BlsSignature, 23 | pub eth1_data: Eth1Data, 24 | pub graffiti: Bytes32, 25 | pub proposer_slashings: List, 26 | pub attester_slashings: 27 | List, MAX_ATTESTER_SLASHINGS>, 28 | pub attestations: List, MAX_ATTESTATIONS>, 29 | pub deposits: List, 30 | pub voluntary_exits: List, 31 | pub sync_aggregate: SyncAggregate, 32 | } 33 | 34 | #[derive( 35 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 36 | )] 37 | pub struct BeaconBlock< 38 | const MAX_PROPOSER_SLASHINGS: usize, 39 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 40 | const MAX_ATTESTER_SLASHINGS: usize, 41 | const MAX_ATTESTATIONS: usize, 42 | const MAX_DEPOSITS: usize, 43 | const MAX_VOLUNTARY_EXITS: usize, 44 | const SYNC_COMMITTEE_SIZE: usize, 45 | > { 46 | #[serde(with = "crate::serde::as_str")] 47 | pub slot: Slot, 48 | #[serde(with = "crate::serde::as_str")] 49 | pub proposer_index: ValidatorIndex, 50 | pub parent_root: Root, 51 | pub state_root: Root, 52 | pub body: BeaconBlockBody< 53 | MAX_PROPOSER_SLASHINGS, 54 | MAX_VALIDATORS_PER_COMMITTEE, 55 | MAX_ATTESTER_SLASHINGS, 56 | MAX_ATTESTATIONS, 57 | MAX_DEPOSITS, 58 | MAX_VOLUNTARY_EXITS, 59 | SYNC_COMMITTEE_SIZE, 60 | >, 61 | } 62 | 63 | #[derive( 64 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 65 | )] 66 | pub struct SignedBeaconBlock< 67 | const MAX_PROPOSER_SLASHINGS: usize, 68 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 69 | const MAX_ATTESTER_SLASHINGS: usize, 70 | const MAX_ATTESTATIONS: usize, 71 | const MAX_DEPOSITS: usize, 72 | const MAX_VOLUNTARY_EXITS: usize, 73 | const SYNC_COMMITTEE_SIZE: usize, 74 | > { 75 | pub message: BeaconBlock< 76 | MAX_PROPOSER_SLASHINGS, 77 | MAX_VALIDATORS_PER_COMMITTEE, 78 | MAX_ATTESTER_SLASHINGS, 79 | MAX_ATTESTATIONS, 80 | MAX_DEPOSITS, 81 | MAX_VOLUNTARY_EXITS, 82 | SYNC_COMMITTEE_SIZE, 83 | >, 84 | pub signature: BlsSignature, 85 | } 86 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bellatrix/fork.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair, 3 | bellatrix::{BeaconState, Fork}, 4 | state_transition::Context, 5 | }; 6 | 7 | pub fn upgrade_to_bellatrix< 8 | const SLOTS_PER_HISTORICAL_ROOT: usize, 9 | const HISTORICAL_ROOTS_LIMIT: usize, 10 | const ETH1_DATA_VOTES_BOUND: usize, 11 | const VALIDATOR_REGISTRY_LIMIT: usize, 12 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 13 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 14 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 15 | const SYNC_COMMITTEE_SIZE: usize, 16 | const BYTES_PER_LOGS_BLOOM: usize, 17 | const MAX_EXTRA_DATA_BYTES: usize, 18 | >( 19 | state: &altair::BeaconState< 20 | SLOTS_PER_HISTORICAL_ROOT, 21 | HISTORICAL_ROOTS_LIMIT, 22 | ETH1_DATA_VOTES_BOUND, 23 | VALIDATOR_REGISTRY_LIMIT, 24 | EPOCHS_PER_HISTORICAL_VECTOR, 25 | EPOCHS_PER_SLASHINGS_VECTOR, 26 | MAX_VALIDATORS_PER_COMMITTEE, 27 | SYNC_COMMITTEE_SIZE, 28 | >, 29 | context: &Context, 30 | ) -> BeaconState< 31 | SLOTS_PER_HISTORICAL_ROOT, 32 | HISTORICAL_ROOTS_LIMIT, 33 | ETH1_DATA_VOTES_BOUND, 34 | VALIDATOR_REGISTRY_LIMIT, 35 | EPOCHS_PER_HISTORICAL_VECTOR, 36 | EPOCHS_PER_SLASHINGS_VECTOR, 37 | MAX_VALIDATORS_PER_COMMITTEE, 38 | SYNC_COMMITTEE_SIZE, 39 | BYTES_PER_LOGS_BLOOM, 40 | MAX_EXTRA_DATA_BYTES, 41 | > { 42 | let epoch = altair::get_current_epoch(state, context); 43 | BeaconState { 44 | genesis_time: state.genesis_time, 45 | genesis_validators_root: state.genesis_validators_root, 46 | slot: state.slot, 47 | fork: Fork { 48 | previous_version: state.fork.current_version, 49 | current_version: context.bellatrix_fork_version, 50 | epoch, 51 | }, 52 | latest_block_header: state.latest_block_header.clone(), 53 | block_roots: state.block_roots.clone(), 54 | state_roots: state.state_roots.clone(), 55 | historical_roots: state.historical_roots.clone(), 56 | eth1_data: state.eth1_data.clone(), 57 | eth1_data_votes: state.eth1_data_votes.clone(), 58 | eth1_deposit_index: state.eth1_deposit_index, 59 | validators: state.validators.clone(), 60 | balances: state.balances.clone(), 61 | randao_mixes: state.randao_mixes.clone(), 62 | slashings: state.slashings.clone(), 63 | previous_epoch_participation: state.previous_epoch_participation.clone(), 64 | current_epoch_participation: state.current_epoch_participation.clone(), 65 | justification_bits: state.justification_bits.clone(), 66 | previous_justified_checkpoint: state.previous_justified_checkpoint.clone(), 67 | current_justified_checkpoint: state.current_justified_checkpoint.clone(), 68 | finalized_checkpoint: state.finalized_checkpoint.clone(), 69 | inactivity_scores: state.inactivity_scores.clone(), 70 | current_sync_committee: state.current_sync_committee.clone(), 71 | next_sync_committee: state.next_sync_committee.clone(), 72 | latest_execution_payload_header: Default::default(), 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /beacon-api-client/examples/api_sketch.rs: -------------------------------------------------------------------------------- 1 | use beacon_api_client::{ApiError, ApiResult, Value, VersionedValue}; 2 | use ethereum_consensus::{ 3 | bellatrix::mainnet as bellatrix, types::mainnet::BlindedBeaconBlock, Fork, 4 | }; 5 | use std::collections::HashMap; 6 | 7 | fn with_fork(fork: Fork, value: T) -> serde_json::Value { 8 | serde_json::json!( { 9 | "version": fork, 10 | "data": value, 11 | }) 12 | } 13 | 14 | fn main() { 15 | let block = Value { meta: HashMap::new(), data: bellatrix::BlindedBeaconBlock::default() }; 16 | let block_repr = serde_json::to_string(&block).unwrap(); 17 | println!("{block_repr}"); 18 | 19 | let version = serde_json::to_value("bellatrix").unwrap(); 20 | let block_with_version = Value { 21 | meta: HashMap::from_iter([("version".to_string(), version)]), 22 | data: bellatrix::BlindedBeaconBlock::default(), 23 | }; 24 | let block_with_version_repr = serde_json::to_string(&block_with_version).unwrap(); 25 | println!("{block_with_version_repr}"); 26 | 27 | let block = BlindedBeaconBlock::Bellatrix(Default::default()); 28 | let block_with_version_repr = 29 | serde_json::to_string(&with_fork(Fork::Bellatrix, &block)).unwrap(); 30 | println!("{block_with_version_repr}"); 31 | let recovered_block: VersionedValue = 32 | serde_json::from_str(&block_with_version_repr).unwrap(); 33 | println!("{recovered_block:#?}"); 34 | 35 | let block = BlindedBeaconBlock::Capella(Default::default()); 36 | let block_with_version_repr = serde_json::to_string(&block).unwrap(); 37 | println!("{block_with_version_repr}"); 38 | 39 | let full_success_response = ApiResult::Ok(VersionedValue { 40 | version: Fork::Capella, 41 | data: block.clone(), 42 | meta: Default::default(), 43 | }); 44 | let str_repr = serde_json::to_string(&full_success_response).unwrap(); 45 | println!("{str_repr}"); 46 | 47 | let recovered_success: ApiResult> = 48 | serde_json::from_str(&str_repr).unwrap(); 49 | println!("{recovered_success:#?}"); 50 | 51 | let full_success_response = ApiResult::Ok(VersionedValue { 52 | version: Fork::Capella, 53 | data: block, 54 | meta: HashMap::from_iter([( 55 | String::from("finalized_root"), 56 | serde_json::Value::String("0xdeadbeefcafe".to_string()), 57 | )]), 58 | }); 59 | let str_repr = serde_json::to_string(&full_success_response).unwrap(); 60 | println!("{str_repr}"); 61 | 62 | let recovered_success: ApiResult> = 63 | serde_json::from_str(&str_repr).unwrap(); 64 | println!("{recovered_success:#?}"); 65 | 66 | let full_error_response: ApiResult> = 67 | ApiResult::Err(ApiError::try_from((404, "some failure")).unwrap()); 68 | let str_repr = serde_json::to_string(&full_error_response).unwrap(); 69 | println!("{str_repr}"); 70 | 71 | let recovered_error: ApiResult = serde_json::from_str(&str_repr).unwrap(); 72 | println!("{recovered_error:#?}"); 73 | } 74 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bin/ec/blobs/encode.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | blobs::{ 3 | framing::{sized_header, Mode as Framing}, 4 | Blob, Error, BITS_PER_FIELD_ELEMENT, BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT, 5 | }, 6 | bls::MODULUS, 7 | }; 8 | use bitvec::prelude::*; 9 | use ethereum_consensus::deneb::presets::mainnet::MAX_BLOBS_PER_BLOCK; 10 | use ruint::aliases::U256; 11 | use std::io::Read; 12 | 13 | type BitSlice = bitvec::slice::BitSlice; 14 | 15 | fn field_element_from_bits(src: &BitSlice) -> Result, Error> { 16 | let mut field_element = vec![0u8; BYTES_PER_FIELD_ELEMENT]; 17 | // first two-bits are unusable via the big-endian field element encoding 18 | let dst = &mut field_element.view_bits_mut()[2..2 + src.len()]; 19 | dst.copy_from_bitslice(src); 20 | 21 | let x = U256::from_be_slice(&field_element); 22 | if x < MODULUS { 23 | Ok(field_element) 24 | } else { 25 | Err(Error::InvalidFieldElement) 26 | } 27 | } 28 | 29 | // Pack a buffer of an arbitrary number of bytes into a series of `Blob`s. 30 | pub fn pack_into_blobs(buffer: &[u8]) -> Result, Error> { 31 | let mut blobs = Vec::with_capacity(MAX_BLOBS_PER_BLOCK); 32 | let bits = BitSlice::from_slice(buffer); 33 | let mut blob_buffer = Vec::with_capacity(BYTES_PER_BLOB); 34 | let mut chunks = bits.chunks_exact(BITS_PER_FIELD_ELEMENT); 35 | for src in chunks.by_ref() { 36 | if blob_buffer.len() == BYTES_PER_BLOB { 37 | let blob = Blob::try_from(blob_buffer.as_ref()).expect("is the right size"); 38 | blobs.push(blob); 39 | blob_buffer.clear(); 40 | } 41 | let mut field_element = field_element_from_bits(src)?; 42 | blob_buffer.append(&mut field_element); 43 | } 44 | 45 | // ensure we have only packed complete field elements so far 46 | assert!(blob_buffer.len() % BYTES_PER_FIELD_ELEMENT == 0); 47 | 48 | let remainder = chunks.remainder(); 49 | if !remainder.is_empty() { 50 | let mut field_element = field_element_from_bits(remainder)?; 51 | blob_buffer.append(&mut field_element); 52 | } 53 | 54 | blob_buffer.resize(BYTES_PER_BLOB, 0); 55 | let blob = Blob::try_from(blob_buffer.as_ref()).expect("is the right size"); 56 | blobs.push(blob); 57 | 58 | Ok(blobs) 59 | } 60 | 61 | // Writes bytes read from `reader` according to `framing` to a sequence of `Blob`s. 62 | // Data is tightly packed into field elements. 63 | pub fn from_reader(mut reader: impl Read, framing: Framing) -> Result, Error> { 64 | let mut buffer = Vec::with_capacity(BYTES_PER_BLOB * MAX_BLOBS_PER_BLOCK); 65 | reader.read_to_end(&mut buffer).expect("can read data"); 66 | let prepared_buffer = match framing { 67 | Framing::Raw => buffer, 68 | Framing::Sized => { 69 | let header = sized_header(buffer.len())?; 70 | let mut framed_buffer = Vec::with_capacity(header.len() + buffer.len()); 71 | framed_buffer.extend_from_slice(&header); 72 | framed_buffer.append(&mut buffer); 73 | framed_buffer 74 | } 75 | }; 76 | pack_into_blobs(&prepared_buffer) 77 | } 78 | -------------------------------------------------------------------------------- /ethereum-consensus/src/deneb/beacon_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::SyncCommittee, 3 | capella::HistoricalSummary, 4 | deneb::ExecutionPayloadHeader, 5 | phase0::{BeaconBlockHeader, Checkpoint, Eth1Data, Fork, Validator, JUSTIFICATION_BITS_LENGTH}, 6 | primitives::{Bytes32, Gwei, ParticipationFlags, Root, Slot, ValidatorIndex, WithdrawalIndex}, 7 | ssz::prelude::*, 8 | }; 9 | 10 | #[derive( 11 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 12 | )] 13 | pub struct BeaconState< 14 | const SLOTS_PER_HISTORICAL_ROOT: usize, 15 | const HISTORICAL_ROOTS_LIMIT: usize, 16 | const ETH1_DATA_VOTES_BOUND: usize, 17 | const VALIDATOR_REGISTRY_LIMIT: usize, 18 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 19 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 20 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 21 | const SYNC_COMMITTEE_SIZE: usize, 22 | const BYTES_PER_LOGS_BLOOM: usize, 23 | const MAX_EXTRA_DATA_BYTES: usize, 24 | > { 25 | #[serde(with = "crate::serde::as_str")] 26 | pub genesis_time: u64, 27 | pub genesis_validators_root: Root, 28 | #[serde(with = "crate::serde::as_str")] 29 | pub slot: Slot, 30 | pub fork: Fork, 31 | pub latest_block_header: BeaconBlockHeader, 32 | pub block_roots: Vector, 33 | pub state_roots: Vector, 34 | pub historical_roots: List, 35 | pub eth1_data: Eth1Data, 36 | pub eth1_data_votes: List, 37 | #[serde(with = "crate::serde::as_str")] 38 | pub eth1_deposit_index: u64, 39 | pub validators: List, 40 | #[serde(with = "crate::serde::seq_of_str")] 41 | pub balances: List, 42 | pub randao_mixes: Vector, 43 | #[serde(with = "crate::serde::seq_of_str")] 44 | pub slashings: Vector, 45 | #[serde(with = "crate::serde::seq_of_str")] 46 | pub previous_epoch_participation: List, 47 | #[serde(with = "crate::serde::seq_of_str")] 48 | pub current_epoch_participation: List, 49 | pub justification_bits: Bitvector, 50 | pub previous_justified_checkpoint: Checkpoint, 51 | pub current_justified_checkpoint: Checkpoint, 52 | pub finalized_checkpoint: Checkpoint, 53 | #[serde(with = "crate::serde::seq_of_str")] 54 | pub inactivity_scores: List, 55 | pub current_sync_committee: SyncCommittee, 56 | pub next_sync_committee: SyncCommittee, 57 | pub latest_execution_payload_header: 58 | ExecutionPayloadHeader, 59 | #[serde(with = "crate::serde::as_str")] 60 | pub next_withdrawal_index: WithdrawalIndex, 61 | #[serde(with = "crate::serde::as_str")] 62 | pub next_withdrawal_validator_index: ValidatorIndex, 63 | pub historical_summaries: List, 64 | } 65 | -------------------------------------------------------------------------------- /ethereum-consensus/src/capella/beacon_state.rs: -------------------------------------------------------------------------------- 1 | pub use crate::phase0::HistoricalSummary; 2 | use crate::{ 3 | altair::SyncCommittee, 4 | capella::ExecutionPayloadHeader, 5 | phase0::{BeaconBlockHeader, Checkpoint, Eth1Data, Fork, Validator, JUSTIFICATION_BITS_LENGTH}, 6 | primitives::{Bytes32, Gwei, ParticipationFlags, Root, Slot, ValidatorIndex, WithdrawalIndex}, 7 | ssz::prelude::*, 8 | }; 9 | 10 | #[derive( 11 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 12 | )] 13 | pub struct BeaconState< 14 | const SLOTS_PER_HISTORICAL_ROOT: usize, 15 | const HISTORICAL_ROOTS_LIMIT: usize, 16 | const ETH1_DATA_VOTES_BOUND: usize, 17 | const VALIDATOR_REGISTRY_LIMIT: usize, 18 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 19 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 20 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 21 | const SYNC_COMMITTEE_SIZE: usize, 22 | const BYTES_PER_LOGS_BLOOM: usize, 23 | const MAX_EXTRA_DATA_BYTES: usize, 24 | > { 25 | #[serde(with = "crate::serde::as_str")] 26 | pub genesis_time: u64, 27 | pub genesis_validators_root: Root, 28 | #[serde(with = "crate::serde::as_str")] 29 | pub slot: Slot, 30 | pub fork: Fork, 31 | pub latest_block_header: BeaconBlockHeader, 32 | pub block_roots: Vector, 33 | pub state_roots: Vector, 34 | pub historical_roots: List, 35 | pub eth1_data: Eth1Data, 36 | pub eth1_data_votes: List, 37 | #[serde(with = "crate::serde::as_str")] 38 | pub eth1_deposit_index: u64, 39 | pub validators: List, 40 | #[serde(with = "crate::serde::seq_of_str")] 41 | pub balances: List, 42 | pub randao_mixes: Vector, 43 | #[serde(with = "crate::serde::seq_of_str")] 44 | pub slashings: Vector, 45 | #[serde(with = "crate::serde::seq_of_str")] 46 | pub previous_epoch_participation: List, 47 | #[serde(with = "crate::serde::seq_of_str")] 48 | pub current_epoch_participation: List, 49 | pub justification_bits: Bitvector, 50 | pub previous_justified_checkpoint: Checkpoint, 51 | pub current_justified_checkpoint: Checkpoint, 52 | pub finalized_checkpoint: Checkpoint, 53 | #[serde(with = "crate::serde::seq_of_str")] 54 | pub inactivity_scores: List, 55 | pub current_sync_committee: SyncCommittee, 56 | pub next_sync_committee: SyncCommittee, 57 | pub latest_execution_payload_header: 58 | ExecutionPayloadHeader, 59 | #[serde(with = "crate::serde::as_str")] 60 | pub next_withdrawal_index: WithdrawalIndex, 61 | #[serde(with = "crate::serde::as_str")] 62 | pub next_withdrawal_validator_index: ValidatorIndex, 63 | pub historical_summaries: List, 64 | } 65 | -------------------------------------------------------------------------------- /ethereum-consensus/src/deneb/light_client.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::light_client::{ 3 | CURRENT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, FINALIZED_ROOT_INDEX_FLOOR_LOG_2, 4 | NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, 5 | }, 6 | capella::EXECUTION_PAYLOAD_INDEX_FLOOR_LOG_2, 7 | deneb::{ 8 | execution_payload::ExecutionPayloadHeader, BeaconBlockHeader, SyncAggregate, SyncCommittee, 9 | }, 10 | primitives::{Bytes32, Slot}, 11 | ssz::prelude::*, 12 | }; 13 | 14 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 15 | pub struct LightClientHeader { 16 | pub beacon: BeaconBlockHeader, 17 | pub execution: ExecutionPayloadHeader, 18 | pub execution_branch: Vector, 19 | } 20 | 21 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 22 | pub struct LightClientBootstrap< 23 | const SYNC_COMMITTEE_SIZE: usize, 24 | const BYTES_PER_LOGS_BLOOM: usize, 25 | const MAX_EXTRA_DATA_BYTES: usize, 26 | > { 27 | pub header: LightClientHeader, 28 | pub current_sync_committee: SyncCommittee, 29 | pub current_sync_committee_branch: Vector, 30 | } 31 | 32 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 33 | pub struct LightClientUpdate< 34 | const SYNC_COMMITTEE_SIZE: usize, 35 | const BYTES_PER_LOGS_BLOOM: usize, 36 | const MAX_EXTRA_DATA_BYTES: usize, 37 | > { 38 | pub attested_header: LightClientHeader, 39 | pub next_sync_committee: SyncCommittee, 40 | pub next_sync_committee_branch: Vector, 41 | pub finalized_header: LightClientHeader, 42 | pub finality_branch: Vector, 43 | pub sync_aggregate: SyncAggregate, 44 | pub signature_slot: Slot, 45 | } 46 | 47 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 48 | pub struct LightClientFinalityUpdate< 49 | const SYNC_COMMITTEE_SIZE: usize, 50 | const BYTES_PER_LOGS_BLOOM: usize, 51 | const MAX_EXTRA_DATA_BYTES: usize, 52 | > { 53 | pub attested_header: LightClientHeader, 54 | pub finalized_header: LightClientHeader, 55 | pub finality_branch: Vector, 56 | pub sync_aggregate: SyncAggregate, 57 | pub signature_slot: Slot, 58 | } 59 | 60 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 61 | pub struct LightClientOptimisticUpdate< 62 | const SYNC_COMMITTEE_SIZE: usize, 63 | const BYTES_PER_LOGS_BLOOM: usize, 64 | const MAX_EXTRA_DATA_BYTES: usize, 65 | > { 66 | pub attested_header: LightClientHeader, 67 | pub sync_aggregate: SyncAggregate, 68 | pub signature_slot: Slot, 69 | } 70 | -------------------------------------------------------------------------------- /ethereum-consensus/src/capella/light_client.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::light_client::{ 3 | CURRENT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, FINALIZED_ROOT_INDEX_FLOOR_LOG_2, 4 | NEXT_SYNC_COMMITTEE_INDEX_FLOOR_LOG_2, 5 | }, 6 | capella::{ 7 | execution_payload::ExecutionPayloadHeader, BeaconBlockHeader, SyncAggregate, SyncCommittee, 8 | }, 9 | primitives::{Bytes32, Slot}, 10 | ssz::prelude::*, 11 | }; 12 | 13 | pub const EXECUTION_PAYLOAD_INDEX: usize = 25; 14 | pub const EXECUTION_PAYLOAD_INDEX_FLOOR_LOG_2: usize = 4; 15 | 16 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 17 | pub struct LightClientHeader { 18 | pub beacon: BeaconBlockHeader, 19 | pub execution: ExecutionPayloadHeader, 20 | pub execution_branch: Vector, 21 | } 22 | 23 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 24 | pub struct LightClientBootstrap< 25 | const SYNC_COMMITTEE_SIZE: usize, 26 | const BYTES_PER_LOGS_BLOOM: usize, 27 | const MAX_EXTRA_DATA_BYTES: usize, 28 | > { 29 | pub header: LightClientHeader, 30 | pub current_sync_committee: SyncCommittee, 31 | pub current_sync_committee_branch: Vector, 32 | } 33 | 34 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 35 | pub struct LightClientUpdate< 36 | const SYNC_COMMITTEE_SIZE: usize, 37 | const BYTES_PER_LOGS_BLOOM: usize, 38 | const MAX_EXTRA_DATA_BYTES: usize, 39 | > { 40 | pub attested_header: LightClientHeader, 41 | pub next_sync_committee: SyncCommittee, 42 | pub next_sync_committee_branch: Vector, 43 | pub finalized_header: LightClientHeader, 44 | pub finality_branch: Vector, 45 | pub sync_aggregate: SyncAggregate, 46 | pub signature_slot: Slot, 47 | } 48 | 49 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 50 | pub struct LightClientFinalityUpdate< 51 | const SYNC_COMMITTEE_SIZE: usize, 52 | const BYTES_PER_LOGS_BLOOM: usize, 53 | const MAX_EXTRA_DATA_BYTES: usize, 54 | > { 55 | pub attested_header: LightClientHeader, 56 | pub finalized_header: LightClientHeader, 57 | pub finality_branch: Vector, 58 | pub sync_aggregate: SyncAggregate, 59 | pub signature_slot: Slot, 60 | } 61 | 62 | #[derive(Default, Debug, Clone, SimpleSerialize, serde::Serialize, serde::Deserialize)] 63 | pub struct LightClientOptimisticUpdate< 64 | const SYNC_COMMITTEE_SIZE: usize, 65 | const BYTES_PER_LOGS_BLOOM: usize, 66 | const MAX_EXTRA_DATA_BYTES: usize, 67 | > { 68 | pub attested_header: LightClientHeader, 69 | pub sync_aggregate: SyncAggregate, 70 | pub signature_slot: Slot, 71 | } 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ethereum-consensus 2 | 3 | A library for interacting with ethereum consensus data. 4 | 5 | # 🚧 WARNING 🚧 6 | 7 | This implementation has **not** been audited for security and is primarily intended for R&D use cases. 8 | 9 | If you need a battle-tested implementation (e.g. for consensus-critical work), refer to the [Lighthouse implementation](https://github.com/sigp/lighthouse). 10 | 11 | # Notes 12 | 13 | ## `ethereum-consensus` 14 | 15 | The `ethereum-consensus` crate exposes a series of modules implementing the [ethereum consensus specs](https://github.com/ethereum/consensus-specs). 16 | 17 | There are a number of top-level modules exposing a variety of supporting types, utilities, etc. 18 | 19 | And then a module to drive any state transition in `state_transition`. 20 | 21 | Specialized logic for each fork can be found in the module with the same name as the fork, e.g. `bellatrix`. 22 | 23 | Each fork supports a compile-time "preset", with two common presets `mainnet` and `minimal` provided as a convenience. 24 | These modules can be found under each fork module in the `presets` module. 25 | 26 | The generic types are exposed but most users will want to access each fork's logic via one of the presets. See 27 | [Examples](#examples) for further details. 28 | 29 | An important thing to note is that the `state_transition` module of each fork (after the `phase0` fork) is generated 30 | by a code-generation utility in the `spec-gen` crate. This utility specializes each fork based on the prior 31 | Rust module as an input. See the README for that binary to learn further details about operating this utility. The 32 | generated files are checked in so you should not need to use this binary under 33 | most circumstances. 34 | 35 | ### Examples 36 | 37 | Refer to the code in `examples` for the suggested way to use this crate as a user. The intermediate types are laid out 38 | so that you can define your own customizations but you can likely use the defaults. 39 | 40 | ### Support for presets 41 | 42 | These crates provide support for the "preset" concept found in the `consensus-specs`. The `minimal` and `mainnet` presets are provided for each fork as hard-coded instances. If you need to support another type of preset, you can make your own using the generic types. Refer to an existing preset, like `ethereum_consensus::bellatrix::presets::minimal`, for an example. 43 | 44 | ### Support for networks 45 | 46 | These crates also support several popular networks. This generally boils down to specific config, for example `ethereum_consensus::configs::goerli::Config`. 47 | 48 | To add support for a new network, you can: 49 | 50 | 1. add a new module under `ethereum_consensus::configs` using an existing network as a template 51 | 2. add the network's `genesis_time` and support for a `Clock` for that network in `ethereum_consensus::clock` 52 | 3. there are convenience methods on `ethereum_consensus::state_transition::Context` for each network and these should also be updated for the new network 53 | 54 | ## `beacon-api-client` 55 | 56 | A client for the Ethereum beacon node APIs: 57 | 58 | https://ethereum.github.io/beacon-APIs 59 | 60 | ### Examples 61 | 62 | Refer to the code in `examples` for a demonstration on how to use the API client. 63 | 64 | ## `spec-gen` 65 | 66 | This crate generates spec code following the "fork diff" defined in each fork of http://github.com/ethereum/consensus-specs. It is not user-facing. 67 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/beacon_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | phase0::operations::{ 3 | Attestation, AttesterSlashing, Deposit, Eth1Data, ProposerSlashing, SignedVoluntaryExit, 4 | }, 5 | primitives::{BlsSignature, Bytes32, Root, Slot, ValidatorIndex}, 6 | ssz::prelude::*, 7 | }; 8 | 9 | #[derive( 10 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 11 | )] 12 | pub struct BeaconBlockBody< 13 | const MAX_PROPOSER_SLASHINGS: usize, 14 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 15 | const MAX_ATTESTER_SLASHINGS: usize, 16 | const MAX_ATTESTATIONS: usize, 17 | const MAX_DEPOSITS: usize, 18 | const MAX_VOLUNTARY_EXITS: usize, 19 | > { 20 | pub randao_reveal: BlsSignature, 21 | pub eth1_data: Eth1Data, 22 | pub graffiti: Bytes32, 23 | pub proposer_slashings: List, 24 | pub attester_slashings: 25 | List, MAX_ATTESTER_SLASHINGS>, 26 | pub attestations: List, MAX_ATTESTATIONS>, 27 | pub deposits: List, 28 | pub voluntary_exits: List, 29 | } 30 | 31 | #[derive( 32 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 33 | )] 34 | pub struct BeaconBlock< 35 | const MAX_PROPOSER_SLASHINGS: usize, 36 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 37 | const MAX_ATTESTER_SLASHINGS: usize, 38 | const MAX_ATTESTATIONS: usize, 39 | const MAX_DEPOSITS: usize, 40 | const MAX_VOLUNTARY_EXITS: usize, 41 | > { 42 | #[serde(with = "crate::serde::as_str")] 43 | pub slot: Slot, 44 | #[serde(with = "crate::serde::as_str")] 45 | pub proposer_index: ValidatorIndex, 46 | pub parent_root: Root, 47 | pub state_root: Root, 48 | pub body: BeaconBlockBody< 49 | MAX_PROPOSER_SLASHINGS, 50 | MAX_VALIDATORS_PER_COMMITTEE, 51 | MAX_ATTESTER_SLASHINGS, 52 | MAX_ATTESTATIONS, 53 | MAX_DEPOSITS, 54 | MAX_VOLUNTARY_EXITS, 55 | >, 56 | } 57 | 58 | #[derive( 59 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 60 | )] 61 | pub struct SignedBeaconBlock< 62 | const MAX_PROPOSER_SLASHINGS: usize, 63 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 64 | const MAX_ATTESTER_SLASHINGS: usize, 65 | const MAX_ATTESTATIONS: usize, 66 | const MAX_DEPOSITS: usize, 67 | const MAX_VOLUNTARY_EXITS: usize, 68 | > { 69 | pub message: BeaconBlock< 70 | MAX_PROPOSER_SLASHINGS, 71 | MAX_VALIDATORS_PER_COMMITTEE, 72 | MAX_ATTESTER_SLASHINGS, 73 | MAX_ATTESTATIONS, 74 | MAX_DEPOSITS, 75 | MAX_VOLUNTARY_EXITS, 76 | >, 77 | pub signature: BlsSignature, 78 | } 79 | 80 | #[derive( 81 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 82 | )] 83 | pub struct BeaconBlockHeader { 84 | #[serde(with = "crate::serde::as_str")] 85 | pub slot: Slot, 86 | #[serde(with = "crate::serde::as_str")] 87 | pub proposer_index: ValidatorIndex, 88 | pub parent_root: Root, 89 | pub state_root: Root, 90 | pub body_root: Root, 91 | } 92 | 93 | #[derive( 94 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 95 | )] 96 | pub struct SignedBeaconBlockHeader { 97 | pub message: BeaconBlockHeader, 98 | pub signature: BlsSignature, 99 | } 100 | -------------------------------------------------------------------------------- /ethereum-consensus/src/capella/epoch_processing.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | capella::{ 3 | get_current_epoch, process_effective_balance_updates, process_eth1_data_reset, 4 | process_inactivity_updates, process_justification_and_finalization, 5 | process_participation_flag_updates, process_randao_mixes_reset, process_registry_updates, 6 | process_rewards_and_penalties, process_slashings, process_slashings_reset, 7 | process_sync_committee_updates, BeaconState, HistoricalSummary, 8 | }, 9 | ssz::prelude::*, 10 | state_transition::{Context, Result}, 11 | }; 12 | 13 | pub fn process_historical_summaries_update< 14 | const SLOTS_PER_HISTORICAL_ROOT: usize, 15 | const HISTORICAL_ROOTS_LIMIT: usize, 16 | const ETH1_DATA_VOTES_BOUND: usize, 17 | const VALIDATOR_REGISTRY_LIMIT: usize, 18 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 19 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 20 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 21 | const SYNC_COMMITTEE_SIZE: usize, 22 | const BYTES_PER_LOGS_BLOOM: usize, 23 | const MAX_EXTRA_DATA_BYTES: usize, 24 | >( 25 | state: &mut BeaconState< 26 | SLOTS_PER_HISTORICAL_ROOT, 27 | HISTORICAL_ROOTS_LIMIT, 28 | ETH1_DATA_VOTES_BOUND, 29 | VALIDATOR_REGISTRY_LIMIT, 30 | EPOCHS_PER_HISTORICAL_VECTOR, 31 | EPOCHS_PER_SLASHINGS_VECTOR, 32 | MAX_VALIDATORS_PER_COMMITTEE, 33 | SYNC_COMMITTEE_SIZE, 34 | BYTES_PER_LOGS_BLOOM, 35 | MAX_EXTRA_DATA_BYTES, 36 | >, 37 | context: &Context, 38 | ) -> Result<()> { 39 | let next_epoch = get_current_epoch(state, context) + 1; 40 | if (next_epoch % (context.slots_per_historical_root / context.slots_per_epoch)) == 0 { 41 | let historical_summary = HistoricalSummary { 42 | block_summary_root: state.block_roots.hash_tree_root()?, 43 | state_summary_root: state.state_roots.hash_tree_root()?, 44 | }; 45 | state.historical_summaries.push(historical_summary); 46 | } 47 | Ok(()) 48 | } 49 | 50 | pub fn process_epoch< 51 | const SLOTS_PER_HISTORICAL_ROOT: usize, 52 | const HISTORICAL_ROOTS_LIMIT: usize, 53 | const ETH1_DATA_VOTES_BOUND: usize, 54 | const VALIDATOR_REGISTRY_LIMIT: usize, 55 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 56 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 57 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 58 | const SYNC_COMMITTEE_SIZE: usize, 59 | const BYTES_PER_LOGS_BLOOM: usize, 60 | const MAX_EXTRA_DATA_BYTES: usize, 61 | >( 62 | state: &mut BeaconState< 63 | SLOTS_PER_HISTORICAL_ROOT, 64 | HISTORICAL_ROOTS_LIMIT, 65 | ETH1_DATA_VOTES_BOUND, 66 | VALIDATOR_REGISTRY_LIMIT, 67 | EPOCHS_PER_HISTORICAL_VECTOR, 68 | EPOCHS_PER_SLASHINGS_VECTOR, 69 | MAX_VALIDATORS_PER_COMMITTEE, 70 | SYNC_COMMITTEE_SIZE, 71 | BYTES_PER_LOGS_BLOOM, 72 | MAX_EXTRA_DATA_BYTES, 73 | >, 74 | context: &Context, 75 | ) -> Result<()> { 76 | process_justification_and_finalization(state, context)?; 77 | process_inactivity_updates(state, context)?; 78 | process_rewards_and_penalties(state, context)?; 79 | process_registry_updates(state, context)?; 80 | process_slashings(state, context)?; 81 | process_eth1_data_reset(state, context); 82 | process_effective_balance_updates(state, context); 83 | process_slashings_reset(state, context); 84 | process_randao_mixes_reset(state, context); 85 | process_historical_summaries_update(state, context)?; 86 | process_participation_flag_updates(state)?; 87 | process_sync_committee_updates(state, context)?; 88 | Ok(()) 89 | } 90 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bellatrix/blinded_beacon_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bellatrix::{ 3 | Attestation, AttesterSlashing, Deposit, Eth1Data, ExecutionPayloadHeader, ProposerSlashing, 4 | SignedVoluntaryExit, SyncAggregate, 5 | }, 6 | primitives::{BlsSignature, Bytes32, Root, Slot, ValidatorIndex}, 7 | ssz::prelude::*, 8 | }; 9 | 10 | #[derive( 11 | Default, Debug, Clone, PartialEq, Eq, SimpleSerialize, serde::Serialize, serde::Deserialize, 12 | )] 13 | pub struct BlindedBeaconBlockBody< 14 | const MAX_PROPOSER_SLASHINGS: usize, 15 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 16 | const MAX_ATTESTER_SLASHINGS: usize, 17 | const MAX_ATTESTATIONS: usize, 18 | const MAX_DEPOSITS: usize, 19 | const MAX_VOLUNTARY_EXITS: usize, 20 | const SYNC_COMMITTEE_SIZE: usize, 21 | const BYTES_PER_LOGS_BLOOM: usize, 22 | const MAX_EXTRA_DATA_BYTES: usize, 23 | > { 24 | pub randao_reveal: BlsSignature, 25 | pub eth1_data: Eth1Data, 26 | pub graffiti: Bytes32, 27 | pub proposer_slashings: List, 28 | pub attester_slashings: 29 | List, MAX_ATTESTER_SLASHINGS>, 30 | pub attestations: List, MAX_ATTESTATIONS>, 31 | pub deposits: List, 32 | pub voluntary_exits: List, 33 | pub sync_aggregate: SyncAggregate, 34 | pub execution_payload_header: 35 | ExecutionPayloadHeader, 36 | } 37 | 38 | #[derive( 39 | Default, Debug, Clone, PartialEq, Eq, SimpleSerialize, serde::Serialize, serde::Deserialize, 40 | )] 41 | pub struct BlindedBeaconBlock< 42 | const MAX_PROPOSER_SLASHINGS: usize, 43 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 44 | const MAX_ATTESTER_SLASHINGS: usize, 45 | const MAX_ATTESTATIONS: usize, 46 | const MAX_DEPOSITS: usize, 47 | const MAX_VOLUNTARY_EXITS: usize, 48 | const SYNC_COMMITTEE_SIZE: usize, 49 | const BYTES_PER_LOGS_BLOOM: usize, 50 | const MAX_EXTRA_DATA_BYTES: usize, 51 | > { 52 | #[serde(with = "crate::serde::as_str")] 53 | pub slot: Slot, 54 | #[serde(with = "crate::serde::as_str")] 55 | pub proposer_index: ValidatorIndex, 56 | pub parent_root: Root, 57 | pub state_root: Root, 58 | pub body: BlindedBeaconBlockBody< 59 | MAX_PROPOSER_SLASHINGS, 60 | MAX_VALIDATORS_PER_COMMITTEE, 61 | MAX_ATTESTER_SLASHINGS, 62 | MAX_ATTESTATIONS, 63 | MAX_DEPOSITS, 64 | MAX_VOLUNTARY_EXITS, 65 | SYNC_COMMITTEE_SIZE, 66 | BYTES_PER_LOGS_BLOOM, 67 | MAX_EXTRA_DATA_BYTES, 68 | >, 69 | } 70 | 71 | #[derive( 72 | Default, Debug, Clone, PartialEq, Eq, SimpleSerialize, serde::Serialize, serde::Deserialize, 73 | )] 74 | pub struct SignedBlindedBeaconBlock< 75 | const MAX_PROPOSER_SLASHINGS: usize, 76 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 77 | const MAX_ATTESTER_SLASHINGS: usize, 78 | const MAX_ATTESTATIONS: usize, 79 | const MAX_DEPOSITS: usize, 80 | const MAX_VOLUNTARY_EXITS: usize, 81 | const SYNC_COMMITTEE_SIZE: usize, 82 | const BYTES_PER_LOGS_BLOOM: usize, 83 | const MAX_EXTRA_DATA_BYTES: usize, 84 | > { 85 | pub message: BlindedBeaconBlock< 86 | MAX_PROPOSER_SLASHINGS, 87 | MAX_VALIDATORS_PER_COMMITTEE, 88 | MAX_ATTESTER_SLASHINGS, 89 | MAX_ATTESTATIONS, 90 | MAX_DEPOSITS, 91 | MAX_VOLUNTARY_EXITS, 92 | SYNC_COMMITTEE_SIZE, 93 | BYTES_PER_LOGS_BLOOM, 94 | MAX_EXTRA_DATA_BYTES, 95 | >, 96 | pub signature: BlsSignature, 97 | } 98 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/presets/minimal.rs: -------------------------------------------------------------------------------- 1 | use crate::altair::spec; 2 | pub use crate::{ 3 | altair::presets::Preset, 4 | phase0::presets::minimal::{ 5 | AggregateAndProof, Attestation, AttesterSlashing, HistoricalBatch, IndexedAttestation, 6 | PendingAttestation, SignedAggregateAndProof, EPOCHS_PER_HISTORICAL_VECTOR, 7 | EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, 8 | MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, MAX_PROPOSER_SLASHINGS, 9 | MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SLOTS_PER_HISTORICAL_ROOT, 10 | VALIDATOR_REGISTRY_LIMIT, 11 | }, 12 | }; 13 | 14 | pub use spec::*; 15 | 16 | pub const INACTIVITY_PENALTY_QUOTIENT_ALTAIR: u64 = 50331648; 17 | pub const MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR: u64 = 64; 18 | pub const PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: u64 = 2; 19 | pub const SYNC_COMMITTEE_SIZE: usize = 32; 20 | pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: Epoch = 8; 21 | pub const MIN_SYNC_COMMITTEE_PARTICIPANTS: usize = 1; 22 | pub const UPDATE_TIMEOUT: usize = 64; 23 | 24 | pub const TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE: usize = 16; 25 | pub const SYNC_SUBCOMMITTEE_SIZE: usize = SYNC_COMMITTEE_SIZE / SYNC_COMMITTEE_SUBNET_COUNT; 26 | 27 | pub const PRESET: Preset = Preset { 28 | inactivity_penalty_quotient_altair: INACTIVITY_PENALTY_QUOTIENT_ALTAIR, 29 | min_slashing_penalty_quotient_altair: MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR, 30 | proportional_slashing_multiplier_altair: PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, 31 | sync_committee_size: SYNC_COMMITTEE_SIZE, 32 | epochs_per_sync_committee_period: EPOCHS_PER_SYNC_COMMITTEE_PERIOD, 33 | min_sync_committee_participants: MIN_SYNC_COMMITTEE_PARTICIPANTS, 34 | update_timeout: UPDATE_TIMEOUT, 35 | }; 36 | 37 | pub type SyncAggregate = spec::SyncAggregate; 38 | pub type SyncCommittee = spec::SyncCommittee; 39 | 40 | pub type BeaconState = spec::BeaconState< 41 | SLOTS_PER_HISTORICAL_ROOT, 42 | HISTORICAL_ROOTS_LIMIT, 43 | ETH1_DATA_VOTES_BOUND, 44 | VALIDATOR_REGISTRY_LIMIT, 45 | EPOCHS_PER_HISTORICAL_VECTOR, 46 | EPOCHS_PER_SLASHINGS_VECTOR, 47 | MAX_VALIDATORS_PER_COMMITTEE, 48 | SYNC_COMMITTEE_SIZE, 49 | >; 50 | 51 | pub type BeaconBlockBody = spec::BeaconBlockBody< 52 | MAX_PROPOSER_SLASHINGS, 53 | MAX_VALIDATORS_PER_COMMITTEE, 54 | MAX_ATTESTER_SLASHINGS, 55 | MAX_ATTESTATIONS, 56 | MAX_DEPOSITS, 57 | MAX_VOLUNTARY_EXITS, 58 | SYNC_COMMITTEE_SIZE, 59 | >; 60 | 61 | pub type BeaconBlock = spec::BeaconBlock< 62 | MAX_PROPOSER_SLASHINGS, 63 | MAX_VALIDATORS_PER_COMMITTEE, 64 | MAX_ATTESTER_SLASHINGS, 65 | MAX_ATTESTATIONS, 66 | MAX_DEPOSITS, 67 | MAX_VOLUNTARY_EXITS, 68 | SYNC_COMMITTEE_SIZE, 69 | >; 70 | 71 | pub type SignedBeaconBlock = spec::SignedBeaconBlock< 72 | MAX_PROPOSER_SLASHINGS, 73 | MAX_VALIDATORS_PER_COMMITTEE, 74 | MAX_ATTESTER_SLASHINGS, 75 | MAX_ATTESTATIONS, 76 | MAX_DEPOSITS, 77 | MAX_VOLUNTARY_EXITS, 78 | SYNC_COMMITTEE_SIZE, 79 | >; 80 | 81 | pub type SyncCommitteeContribution = spec::SyncCommitteeContribution; 82 | pub type ContributionAndProof = spec::ContributionAndProof; 83 | pub type SignedContributionAndProof = spec::SignedContributionAndProof; 84 | 85 | pub type LightClientBootstrap = spec::LightClientBootstrap; 86 | pub type LightClientUpdate = spec::LightClientUpdate; 87 | pub type LightClientFinalityUpdate = spec::LightClientFinalityUpdate; 88 | pub type LightClientOptimisticUpdate = spec::LightClientOptimisticUpdate; 89 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/presets/mainnet.rs: -------------------------------------------------------------------------------- 1 | use crate::altair::spec; 2 | pub use crate::{ 3 | altair::presets::Preset, 4 | phase0::presets::mainnet::{ 5 | AggregateAndProof, Attestation, AttesterSlashing, HistoricalBatch, IndexedAttestation, 6 | PendingAttestation, SignedAggregateAndProof, EPOCHS_PER_HISTORICAL_VECTOR, 7 | EPOCHS_PER_SLASHINGS_VECTOR, ETH1_DATA_VOTES_BOUND, HISTORICAL_ROOTS_LIMIT, 8 | MAX_ATTESTATIONS, MAX_ATTESTER_SLASHINGS, MAX_DEPOSITS, MAX_PROPOSER_SLASHINGS, 9 | MAX_VALIDATORS_PER_COMMITTEE, MAX_VOLUNTARY_EXITS, SLOTS_PER_HISTORICAL_ROOT, 10 | VALIDATOR_REGISTRY_LIMIT, 11 | }, 12 | }; 13 | 14 | pub use spec::*; 15 | 16 | pub const INACTIVITY_PENALTY_QUOTIENT_ALTAIR: u64 = 50331648; 17 | pub const MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR: u64 = 64; 18 | pub const PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR: u64 = 2; 19 | pub const SYNC_COMMITTEE_SIZE: usize = 512; 20 | pub const EPOCHS_PER_SYNC_COMMITTEE_PERIOD: Epoch = 256; 21 | pub const MIN_SYNC_COMMITTEE_PARTICIPANTS: usize = 1; 22 | pub const UPDATE_TIMEOUT: usize = 8192; 23 | 24 | pub const TARGET_AGGREGATORS_PER_SYNC_SUBCOMMITTEE: usize = 16; 25 | pub const SYNC_SUBCOMMITTEE_SIZE: usize = SYNC_COMMITTEE_SIZE / SYNC_COMMITTEE_SUBNET_COUNT; 26 | 27 | pub const PRESET: Preset = Preset { 28 | inactivity_penalty_quotient_altair: INACTIVITY_PENALTY_QUOTIENT_ALTAIR, 29 | min_slashing_penalty_quotient_altair: MIN_SLASHING_PENALTY_QUOTIENT_ALTAIR, 30 | proportional_slashing_multiplier_altair: PROPORTIONAL_SLASHING_MULTIPLIER_ALTAIR, 31 | sync_committee_size: SYNC_COMMITTEE_SIZE, 32 | epochs_per_sync_committee_period: EPOCHS_PER_SYNC_COMMITTEE_PERIOD, 33 | min_sync_committee_participants: MIN_SYNC_COMMITTEE_PARTICIPANTS, 34 | update_timeout: UPDATE_TIMEOUT, 35 | }; 36 | 37 | pub type SyncAggregate = spec::SyncAggregate; 38 | pub type SyncCommittee = spec::SyncCommittee; 39 | 40 | pub type BeaconState = spec::BeaconState< 41 | SLOTS_PER_HISTORICAL_ROOT, 42 | HISTORICAL_ROOTS_LIMIT, 43 | ETH1_DATA_VOTES_BOUND, 44 | VALIDATOR_REGISTRY_LIMIT, 45 | EPOCHS_PER_HISTORICAL_VECTOR, 46 | EPOCHS_PER_SLASHINGS_VECTOR, 47 | MAX_VALIDATORS_PER_COMMITTEE, 48 | SYNC_COMMITTEE_SIZE, 49 | >; 50 | 51 | pub type BeaconBlockBody = spec::BeaconBlockBody< 52 | MAX_PROPOSER_SLASHINGS, 53 | MAX_VALIDATORS_PER_COMMITTEE, 54 | MAX_ATTESTER_SLASHINGS, 55 | MAX_ATTESTATIONS, 56 | MAX_DEPOSITS, 57 | MAX_VOLUNTARY_EXITS, 58 | SYNC_COMMITTEE_SIZE, 59 | >; 60 | 61 | pub type BeaconBlock = spec::BeaconBlock< 62 | MAX_PROPOSER_SLASHINGS, 63 | MAX_VALIDATORS_PER_COMMITTEE, 64 | MAX_ATTESTER_SLASHINGS, 65 | MAX_ATTESTATIONS, 66 | MAX_DEPOSITS, 67 | MAX_VOLUNTARY_EXITS, 68 | SYNC_COMMITTEE_SIZE, 69 | >; 70 | 71 | pub type SignedBeaconBlock = spec::SignedBeaconBlock< 72 | MAX_PROPOSER_SLASHINGS, 73 | MAX_VALIDATORS_PER_COMMITTEE, 74 | MAX_ATTESTER_SLASHINGS, 75 | MAX_ATTESTATIONS, 76 | MAX_DEPOSITS, 77 | MAX_VOLUNTARY_EXITS, 78 | SYNC_COMMITTEE_SIZE, 79 | >; 80 | 81 | pub type SyncCommitteeContribution = spec::SyncCommitteeContribution; 82 | pub type ContributionAndProof = spec::ContributionAndProof; 83 | pub type SignedContributionAndProof = spec::SignedContributionAndProof; 84 | 85 | pub type LightClientBootstrap = spec::LightClientBootstrap; 86 | pub type LightClientUpdate = spec::LightClientUpdate; 87 | pub type LightClientFinalityUpdate = spec::LightClientFinalityUpdate; 88 | pub type LightClientOptimisticUpdate = spec::LightClientOptimisticUpdate; 89 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/beacon_state.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | phase0::{ 3 | beacon_block::BeaconBlockHeader, 4 | constants::JUSTIFICATION_BITS_LENGTH, 5 | operations::{Checkpoint, Eth1Data, PendingAttestation}, 6 | validator::Validator, 7 | }, 8 | primitives::{Bytes32, Epoch, Gwei, Root, Slot, Version}, 9 | ssz::prelude::*, 10 | }; 11 | 12 | #[derive( 13 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 14 | )] 15 | pub struct Fork { 16 | #[serde(with = "crate::serde::as_hex")] 17 | pub previous_version: Version, 18 | #[serde(with = "crate::serde::as_hex")] 19 | pub current_version: Version, 20 | #[serde(with = "crate::serde::as_str")] 21 | pub epoch: Epoch, 22 | } 23 | 24 | #[derive(Default, Debug, SimpleSerialize, Clone, serde::Serialize, serde::Deserialize)] 25 | pub struct ForkData { 26 | #[serde(with = "crate::serde::as_hex")] 27 | pub current_version: Version, 28 | pub genesis_validators_root: Root, 29 | } 30 | 31 | #[derive(Default, Debug, SimpleSerialize, serde::Serialize, serde::Deserialize)] 32 | pub struct HistoricalBatch { 33 | pub block_roots: Vector, 34 | pub state_roots: Vector, 35 | } 36 | 37 | // Note: `HistoricalSummary` is defined in the `capella` specs; however, this // repo used the same 38 | // strategy to compute the `HistoricalBatch` roots so // the type already existed. 39 | #[derive( 40 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 41 | )] 42 | pub struct HistoricalSummary { 43 | pub block_summary_root: Root, 44 | pub state_summary_root: Root, 45 | } 46 | 47 | #[derive( 48 | Default, Debug, SimpleSerialize, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, 49 | )] 50 | pub struct BeaconState< 51 | const SLOTS_PER_HISTORICAL_ROOT: usize, 52 | const HISTORICAL_ROOTS_LIMIT: usize, 53 | const ETH1_DATA_VOTES_BOUND: usize, 54 | const VALIDATOR_REGISTRY_LIMIT: usize, 55 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 56 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 57 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 58 | const PENDING_ATTESTATIONS_BOUND: usize, 59 | > { 60 | #[serde(with = "crate::serde::as_str")] 61 | pub genesis_time: u64, 62 | pub genesis_validators_root: Root, 63 | #[serde(with = "crate::serde::as_str")] 64 | pub slot: Slot, 65 | pub fork: Fork, 66 | pub latest_block_header: BeaconBlockHeader, 67 | pub block_roots: Vector, 68 | pub state_roots: Vector, 69 | pub historical_roots: List, 70 | pub eth1_data: Eth1Data, 71 | pub eth1_data_votes: List, 72 | #[serde(with = "crate::serde::as_str")] 73 | pub eth1_deposit_index: u64, 74 | pub validators: List, 75 | #[serde(with = "crate::serde::seq_of_str")] 76 | pub balances: List, 77 | pub randao_mixes: Vector, 78 | #[serde(with = "crate::serde::seq_of_str")] 79 | pub slashings: Vector, 80 | pub previous_epoch_attestations: 81 | List, PENDING_ATTESTATIONS_BOUND>, 82 | pub current_epoch_attestations: 83 | List, PENDING_ATTESTATIONS_BOUND>, 84 | pub justification_bits: Bitvector, 85 | pub previous_justified_checkpoint: Checkpoint, 86 | pub current_justified_checkpoint: Checkpoint, 87 | pub finalized_checkpoint: Checkpoint, 88 | } 89 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/spec/mod.rs: -------------------------------------------------------------------------------- 1 | //! WARNING: This file was derived by the `spec-gen` utility. DO NOT EDIT MANUALLY. 2 | pub use crate::{ 3 | error::*, 4 | phase0::{ 5 | beacon_block::{ 6 | BeaconBlock, BeaconBlockBody, BeaconBlockHeader, SignedBeaconBlock, 7 | SignedBeaconBlockHeader, 8 | }, 9 | beacon_state::{BeaconState, Fork, ForkData, HistoricalBatch, HistoricalSummary}, 10 | block_processing::{ 11 | add_validator_to_registry, apply_deposit, get_validator_from_deposit, 12 | process_attestation, process_attester_slashing, process_block, process_block_header, 13 | process_deposit, process_eth1_data, process_operations, process_proposer_slashing, 14 | process_randao, process_voluntary_exit, xor, 15 | }, 16 | constants::{ 17 | BASE_REWARDS_PER_EPOCH, DEPOSIT_CONTRACT_TREE_DEPTH, DEPOSIT_DATA_LIST_BOUND, 18 | JUSTIFICATION_BITS_LENGTH, 19 | }, 20 | epoch_processing::{ 21 | get_attestation_component_deltas, get_attestation_deltas, get_attesting_balance, 22 | get_base_reward, get_finality_delay, get_head_deltas, get_inactivity_penalty_deltas, 23 | get_inclusion_delay_deltas, get_matching_head_attestations, 24 | get_matching_source_attestations, get_matching_target_attestations, 25 | get_proposer_reward, get_source_deltas, get_target_deltas, 26 | get_unslashed_attesting_indices, is_in_inactivity_leak, 27 | process_effective_balance_updates, process_epoch, process_eth1_data_reset, 28 | process_historical_roots_update, process_justification_and_finalization, 29 | process_participation_record_updates, process_randao_mixes_reset, 30 | process_registry_updates, process_rewards_and_penalties, process_slashings, 31 | process_slashings_reset, weigh_justification_and_finalization, 32 | }, 33 | genesis::{get_genesis_block, initialize_beacon_state_from_eth1, is_valid_genesis_state}, 34 | helpers::{ 35 | compute_activation_exit_epoch, compute_committee, compute_domain, 36 | compute_epoch_at_slot, compute_fork_data_root, compute_fork_digest, 37 | compute_proposer_index, compute_shuffled_index, compute_shuffled_indices, 38 | compute_start_slot_at_epoch, decrease_balance, get_active_validator_indices, 39 | get_attesting_indices, get_beacon_committee, get_beacon_proposer_index, get_block_root, 40 | get_block_root_at_slot, get_committee_count_per_slot, get_current_epoch, get_domain, 41 | get_eligible_validator_indices, get_indexed_attestation, get_previous_epoch, 42 | get_randao_mix, get_seed, get_total_active_balance, get_total_balance, 43 | get_validator_churn_limit, increase_balance, initiate_validator_exit, 44 | is_active_validator, is_eligible_for_activation, is_eligible_for_activation_queue, 45 | is_slashable_attestation_data, is_slashable_validator, is_valid_indexed_attestation, 46 | sample_proposer_index, slash_validator, verify_block_signature, 47 | }, 48 | operations::{ 49 | Attestation, AttestationData, AttesterSlashing, Checkpoint, Deposit, DepositData, 50 | DepositMessage, Eth1Data, IndexedAttestation, PendingAttestation, ProposerSlashing, 51 | SignedVoluntaryExit, VoluntaryExit, 52 | }, 53 | slot_processing::{process_slot, process_slots}, 54 | state_transition::{state_transition, state_transition_block_in_slot}, 55 | validator::{AggregateAndProof, Eth1Block, SignedAggregateAndProof, Validator}, 56 | }, 57 | primitives::*, 58 | signing::*, 59 | state_transition::{Context, Result, Validation}, 60 | }; 61 | -------------------------------------------------------------------------------- /spec-tests/runners/light_client.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | runners::gen_match_for, 3 | test_case::TestCase, 4 | test_meta::TestMeta, 5 | test_utils::{load_snappy_ssz, load_yaml, Error}, 6 | Fork, 7 | }; 8 | use ethereum_consensus::Error as SpecError; 9 | use serde::Deserialize; 10 | use ssz_rs::{ 11 | prelude::*, 12 | proofs::{get_subtree_index, is_valid_merkle_branch_for_generalized_index, log_2}, 13 | }; 14 | 15 | #[derive(Debug, Deserialize)] 16 | pub struct Proof { 17 | leaf: Node, 18 | leaf_index: GeneralizedIndex, 19 | branch: Vec, 20 | } 21 | 22 | pub fn load_test(test_case_path: &str) -> (O, Proof) { 23 | let path = test_case_path.to_string() + "/object.ssz_snappy"; 24 | let object: O = load_snappy_ssz(&path).unwrap(); 25 | 26 | let path = test_case_path.to_string() + "/proof.yaml"; 27 | let proof: Proof = load_yaml(&path); 28 | 29 | (object, proof) 30 | } 31 | 32 | fn path_from(meta: &TestMeta) -> Vec { 33 | match meta.case.0.strip_suffix("_merkle_proof").unwrap() { 34 | "current_sync_committee" => vec!["current_sync_committee".into()], 35 | "finality_root" => vec!["finalized_checkpoint".into(), "root".into()], 36 | "next_sync_committee" => vec!["next_sync_committee".into()], 37 | "execution" => vec!["execution_payload".into()], 38 | elem => unimplemented!("unsupported proof element `{elem}`"), 39 | } 40 | } 41 | 42 | pub fn run_test(object: O, path: Path, proof: &Proof) -> Result<(), Error> { 43 | let root = object.hash_tree_root().unwrap(); 44 | // test proof matches 45 | let (computed_proof, witness) = object.prove(path).expect("can prove"); 46 | assert_eq!(root, witness); 47 | assert_eq!(proof.leaf, computed_proof.leaf); 48 | assert_eq!(proof.leaf_index, computed_proof.index); 49 | assert_eq!(proof.branch, computed_proof.branch); 50 | assert!(computed_proof.verify(witness).is_ok()); 51 | 52 | // test generalized index verifier 53 | assert!(is_valid_merkle_branch_for_generalized_index( 54 | proof.leaf, 55 | &proof.branch, 56 | proof.leaf_index, 57 | root 58 | ) 59 | .is_ok()); 60 | // test regular index verifier 61 | is_valid_merkle_branch( 62 | proof.leaf, 63 | &proof.branch, 64 | log_2(proof.leaf_index).unwrap() as usize, 65 | get_subtree_index(proof.leaf_index).unwrap(), 66 | root, 67 | ) 68 | .map_err(|err| SpecError::from(err).into()) 69 | } 70 | 71 | pub fn dispatch(test: &TestCase) -> Result<(), Error> { 72 | match test.meta.handler.0.as_str() { 73 | "single_merkle_proof" => { 74 | gen_match_for! { 75 | test, 76 | (mainnet, altair), 77 | (mainnet, bellatrix), 78 | (mainnet, capella), 79 | (mainnet, deneb), 80 | (minimal, altair), 81 | (minimal, bellatrix), 82 | (minimal, capella), 83 | (minimal, deneb) 84 | { 85 | if matches!(test.meta.fork, Fork::Capella | Fork::Deneb) && test.meta.suite.0 == "BeaconBlockBody" { 86 | let (object, proof) = load_test::(&test.data_path); 87 | let path = path_from(&test.meta); 88 | return run_test(object, &path, &proof); 89 | } 90 | 91 | let (object, proof) = load_test::(&test.data_path); 92 | let path = path_from(&test.meta); 93 | run_test(object, &path, &proof) 94 | } 95 | } 96 | } 97 | handler => unreachable!("no tests for {handler}"), 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /ethereum-consensus/src/phase0/state_transition.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | phase0::{ 3 | beacon_block::SignedBeaconBlock, beacon_state::BeaconState, 4 | block_processing::process_block, helpers::verify_block_signature, 5 | slot_processing::process_slots, 6 | }, 7 | ssz::prelude::*, 8 | state_transition::{Context, Result, Validation}, 9 | Error, 10 | }; 11 | 12 | // `state_transition_block_in_slot` is separated out 13 | // to facilitate upgrades across forks which take place 14 | // after the call to `process_slots` in the sub-transition. 15 | pub fn state_transition_block_in_slot< 16 | const SLOTS_PER_HISTORICAL_ROOT: usize, 17 | const HISTORICAL_ROOTS_LIMIT: usize, 18 | const ETH1_DATA_VOTES_BOUND: usize, 19 | const VALIDATOR_REGISTRY_LIMIT: usize, 20 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 21 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 22 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 23 | const PENDING_ATTESTATIONS_BOUND: usize, 24 | const MAX_PROPOSER_SLASHINGS: usize, 25 | const MAX_ATTESTER_SLASHINGS: usize, 26 | const MAX_ATTESTATIONS: usize, 27 | const MAX_DEPOSITS: usize, 28 | const MAX_VOLUNTARY_EXITS: usize, 29 | >( 30 | state: &mut BeaconState< 31 | SLOTS_PER_HISTORICAL_ROOT, 32 | HISTORICAL_ROOTS_LIMIT, 33 | ETH1_DATA_VOTES_BOUND, 34 | VALIDATOR_REGISTRY_LIMIT, 35 | EPOCHS_PER_HISTORICAL_VECTOR, 36 | EPOCHS_PER_SLASHINGS_VECTOR, 37 | MAX_VALIDATORS_PER_COMMITTEE, 38 | PENDING_ATTESTATIONS_BOUND, 39 | >, 40 | signed_block: &SignedBeaconBlock< 41 | MAX_PROPOSER_SLASHINGS, 42 | MAX_VALIDATORS_PER_COMMITTEE, 43 | MAX_ATTESTER_SLASHINGS, 44 | MAX_ATTESTATIONS, 45 | MAX_DEPOSITS, 46 | MAX_VOLUNTARY_EXITS, 47 | >, 48 | validation: Validation, 49 | context: &Context, 50 | ) -> Result<()> { 51 | let validate_result = match validation { 52 | Validation::Enabled => true, 53 | Validation::Disabled => false, 54 | }; 55 | if validate_result { 56 | verify_block_signature(state, signed_block, context)?; 57 | } 58 | let block = &signed_block.message; 59 | process_block(state, block, context)?; 60 | if validate_result && block.state_root != state.hash_tree_root()? { 61 | Err(Error::InvalidStateRoot) 62 | } else { 63 | Ok(()) 64 | } 65 | } 66 | 67 | pub fn state_transition< 68 | const SLOTS_PER_HISTORICAL_ROOT: usize, 69 | const HISTORICAL_ROOTS_LIMIT: usize, 70 | const ETH1_DATA_VOTES_BOUND: usize, 71 | const VALIDATOR_REGISTRY_LIMIT: usize, 72 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 73 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 74 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 75 | const PENDING_ATTESTATIONS_BOUND: usize, 76 | const MAX_PROPOSER_SLASHINGS: usize, 77 | const MAX_ATTESTER_SLASHINGS: usize, 78 | const MAX_ATTESTATIONS: usize, 79 | const MAX_DEPOSITS: usize, 80 | const MAX_VOLUNTARY_EXITS: usize, 81 | >( 82 | state: &mut BeaconState< 83 | SLOTS_PER_HISTORICAL_ROOT, 84 | HISTORICAL_ROOTS_LIMIT, 85 | ETH1_DATA_VOTES_BOUND, 86 | VALIDATOR_REGISTRY_LIMIT, 87 | EPOCHS_PER_HISTORICAL_VECTOR, 88 | EPOCHS_PER_SLASHINGS_VECTOR, 89 | MAX_VALIDATORS_PER_COMMITTEE, 90 | PENDING_ATTESTATIONS_BOUND, 91 | >, 92 | signed_block: &SignedBeaconBlock< 93 | MAX_PROPOSER_SLASHINGS, 94 | MAX_VALIDATORS_PER_COMMITTEE, 95 | MAX_ATTESTER_SLASHINGS, 96 | MAX_ATTESTATIONS, 97 | MAX_DEPOSITS, 98 | MAX_VOLUNTARY_EXITS, 99 | >, 100 | validation: Validation, 101 | context: &Context, 102 | ) -> Result<()> { 103 | process_slots(state, signed_block.message.slot, context)?; 104 | 105 | state_transition_block_in_slot(state, signed_block, validation, context) 106 | } 107 | -------------------------------------------------------------------------------- /ethereum-consensus/src/altair/genesis.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::{ 3 | beacon_block::BeaconBlockBody, beacon_state::BeaconState, helpers::get_next_sync_committee, 4 | process_deposit, BeaconBlockHeader, Deposit, DepositData, Eth1Data, Fork, 5 | DEPOSIT_DATA_LIST_BOUND, 6 | }, 7 | primitives::{Gwei, Hash32, GENESIS_EPOCH}, 8 | ssz::prelude::*, 9 | state_transition::{Context, Result}, 10 | }; 11 | 12 | pub fn initialize_beacon_state_from_eth1< 13 | const SLOTS_PER_HISTORICAL_ROOT: usize, 14 | const HISTORICAL_ROOTS_LIMIT: usize, 15 | const ETH1_DATA_VOTES_BOUND: usize, 16 | const VALIDATOR_REGISTRY_LIMIT: usize, 17 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 18 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 19 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 20 | const SYNC_COMMITTEE_SIZE: usize, 21 | const MAX_PROPOSER_SLASHINGS: usize, 22 | const MAX_ATTESTER_SLASHINGS: usize, 23 | const MAX_ATTESTATIONS: usize, 24 | const MAX_DEPOSITS: usize, 25 | const MAX_VOLUNTARY_EXITS: usize, 26 | >( 27 | eth1_block_hash: Hash32, 28 | eth1_timestamp: u64, 29 | deposits: &[Deposit], 30 | context: &Context, 31 | ) -> Result< 32 | BeaconState< 33 | SLOTS_PER_HISTORICAL_ROOT, 34 | HISTORICAL_ROOTS_LIMIT, 35 | ETH1_DATA_VOTES_BOUND, 36 | VALIDATOR_REGISTRY_LIMIT, 37 | EPOCHS_PER_HISTORICAL_VECTOR, 38 | EPOCHS_PER_SLASHINGS_VECTOR, 39 | MAX_VALIDATORS_PER_COMMITTEE, 40 | SYNC_COMMITTEE_SIZE, 41 | >, 42 | > { 43 | let fork = Fork { 44 | previous_version: context.altair_fork_version, 45 | current_version: context.altair_fork_version, 46 | epoch: GENESIS_EPOCH, 47 | }; 48 | let eth1_data = Eth1Data { 49 | block_hash: eth1_block_hash.clone(), 50 | deposit_count: deposits.len() as u64, 51 | ..Default::default() 52 | }; 53 | let latest_block_body = BeaconBlockBody::< 54 | MAX_PROPOSER_SLASHINGS, 55 | MAX_VALIDATORS_PER_COMMITTEE, 56 | MAX_ATTESTER_SLASHINGS, 57 | MAX_ATTESTATIONS, 58 | MAX_DEPOSITS, 59 | MAX_VOLUNTARY_EXITS, 60 | SYNC_COMMITTEE_SIZE, 61 | >::default(); 62 | let body_root = latest_block_body.hash_tree_root()?; 63 | let latest_block_header = BeaconBlockHeader { body_root, ..Default::default() }; 64 | let randao_mixes = Vector::try_from( 65 | std::iter::repeat_n(eth1_block_hash, context.epochs_per_historical_vector as usize) 66 | .collect::>(), 67 | ) 68 | .map_err(|(_, err)| err)?; 69 | let mut state = BeaconState { 70 | genesis_time: eth1_timestamp + context.genesis_delay, 71 | fork, 72 | eth1_data, 73 | latest_block_header, 74 | randao_mixes, 75 | ..Default::default() 76 | }; 77 | 78 | let mut leaves = List::::default(); 79 | for deposit in deposits.iter() { 80 | leaves.push(deposit.data.clone()); 81 | state.eth1_data.deposit_root = leaves.hash_tree_root()?; 82 | process_deposit(&mut state, deposit, context)?; 83 | } 84 | 85 | for i in 0..state.validators.len() { 86 | let validator = &mut state.validators[i]; 87 | let balance = state.balances[i]; 88 | let effective_balance = Gwei::min( 89 | balance - balance % context.effective_balance_increment, 90 | context.max_effective_balance, 91 | ); 92 | validator.effective_balance = effective_balance; 93 | if validator.effective_balance == context.max_effective_balance { 94 | validator.activation_eligibility_epoch = GENESIS_EPOCH; 95 | validator.activation_epoch = GENESIS_EPOCH; 96 | } 97 | } 98 | 99 | state.genesis_validators_root = state.validators.hash_tree_root()?; 100 | 101 | let sync_committee = get_next_sync_committee(&state, context)?; 102 | state.current_sync_committee = sync_committee.clone(); 103 | state.next_sync_committee = sync_committee; 104 | 105 | Ok(state) 106 | } 107 | -------------------------------------------------------------------------------- /ethereum-consensus/src/capella/blinded_beacon_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | altair::SyncAggregate, 3 | capella::{ExecutionPayloadHeader, SignedBlsToExecutionChange}, 4 | phase0::{ 5 | Attestation, AttesterSlashing, Deposit, Eth1Data, ProposerSlashing, SignedVoluntaryExit, 6 | }, 7 | primitives::{BlsSignature, Bytes32, Root, Slot, ValidatorIndex}, 8 | ssz::prelude::*, 9 | }; 10 | 11 | #[derive( 12 | Default, Debug, Clone, PartialEq, Eq, SimpleSerialize, serde::Serialize, serde::Deserialize, 13 | )] 14 | pub struct BlindedBeaconBlockBody< 15 | const MAX_PROPOSER_SLASHINGS: usize, 16 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 17 | const MAX_ATTESTER_SLASHINGS: usize, 18 | const MAX_ATTESTATIONS: usize, 19 | const MAX_DEPOSITS: usize, 20 | const MAX_VOLUNTARY_EXITS: usize, 21 | const SYNC_COMMITTEE_SIZE: usize, 22 | const BYTES_PER_LOGS_BLOOM: usize, 23 | const MAX_EXTRA_DATA_BYTES: usize, 24 | const MAX_BLS_TO_EXECUTION_CHANGES: usize, 25 | > { 26 | pub randao_reveal: BlsSignature, 27 | pub eth1_data: Eth1Data, 28 | pub graffiti: Bytes32, 29 | pub proposer_slashings: List, 30 | pub attester_slashings: 31 | List, MAX_ATTESTER_SLASHINGS>, 32 | pub attestations: List, MAX_ATTESTATIONS>, 33 | pub deposits: List, 34 | pub voluntary_exits: List, 35 | pub sync_aggregate: SyncAggregate, 36 | pub execution_payload_header: 37 | ExecutionPayloadHeader, 38 | pub bls_to_execution_changes: List, 39 | } 40 | 41 | #[derive( 42 | Default, Debug, Clone, PartialEq, Eq, SimpleSerialize, serde::Serialize, serde::Deserialize, 43 | )] 44 | pub struct BlindedBeaconBlock< 45 | const MAX_PROPOSER_SLASHINGS: usize, 46 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 47 | const MAX_ATTESTER_SLASHINGS: usize, 48 | const MAX_ATTESTATIONS: usize, 49 | const MAX_DEPOSITS: usize, 50 | const MAX_VOLUNTARY_EXITS: usize, 51 | const SYNC_COMMITTEE_SIZE: usize, 52 | const BYTES_PER_LOGS_BLOOM: usize, 53 | const MAX_EXTRA_DATA_BYTES: usize, 54 | const MAX_BLS_TO_EXECUTION_CHANGES: usize, 55 | > { 56 | #[serde(with = "crate::serde::as_str")] 57 | pub slot: Slot, 58 | #[serde(with = "crate::serde::as_str")] 59 | pub proposer_index: ValidatorIndex, 60 | pub parent_root: Root, 61 | pub state_root: Root, 62 | pub body: BlindedBeaconBlockBody< 63 | MAX_PROPOSER_SLASHINGS, 64 | MAX_VALIDATORS_PER_COMMITTEE, 65 | MAX_ATTESTER_SLASHINGS, 66 | MAX_ATTESTATIONS, 67 | MAX_DEPOSITS, 68 | MAX_VOLUNTARY_EXITS, 69 | SYNC_COMMITTEE_SIZE, 70 | BYTES_PER_LOGS_BLOOM, 71 | MAX_EXTRA_DATA_BYTES, 72 | MAX_BLS_TO_EXECUTION_CHANGES, 73 | >, 74 | } 75 | 76 | #[derive( 77 | Default, Debug, Clone, PartialEq, Eq, SimpleSerialize, serde::Serialize, serde::Deserialize, 78 | )] 79 | pub struct SignedBlindedBeaconBlock< 80 | const MAX_PROPOSER_SLASHINGS: usize, 81 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 82 | const MAX_ATTESTER_SLASHINGS: usize, 83 | const MAX_ATTESTATIONS: usize, 84 | const MAX_DEPOSITS: usize, 85 | const MAX_VOLUNTARY_EXITS: usize, 86 | const SYNC_COMMITTEE_SIZE: usize, 87 | const BYTES_PER_LOGS_BLOOM: usize, 88 | const MAX_EXTRA_DATA_BYTES: usize, 89 | const MAX_BLS_TO_EXECUTION_CHANGES: usize, 90 | > { 91 | pub message: BlindedBeaconBlock< 92 | MAX_PROPOSER_SLASHINGS, 93 | MAX_VALIDATORS_PER_COMMITTEE, 94 | MAX_ATTESTER_SLASHINGS, 95 | MAX_ATTESTATIONS, 96 | MAX_DEPOSITS, 97 | MAX_VOLUNTARY_EXITS, 98 | SYNC_COMMITTEE_SIZE, 99 | BYTES_PER_LOGS_BLOOM, 100 | MAX_EXTRA_DATA_BYTES, 101 | MAX_BLS_TO_EXECUTION_CHANGES, 102 | >, 103 | pub signature: BlsSignature, 104 | } 105 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bellatrix/epoch_processing.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bellatrix::{ 3 | decrease_balance, get_current_epoch, get_total_active_balance, 4 | process_effective_balance_updates, process_eth1_data_reset, 5 | process_historical_roots_update, process_inactivity_updates, 6 | process_justification_and_finalization, process_participation_flag_updates, 7 | process_randao_mixes_reset, process_registry_updates, process_rewards_and_penalties, 8 | process_slashings_reset, process_sync_committee_updates, BeaconState, 9 | }, 10 | primitives::Gwei, 11 | state_transition::{Context, Result}, 12 | }; 13 | 14 | pub fn process_slashings< 15 | const SLOTS_PER_HISTORICAL_ROOT: usize, 16 | const HISTORICAL_ROOTS_LIMIT: usize, 17 | const ETH1_DATA_VOTES_BOUND: usize, 18 | const VALIDATOR_REGISTRY_LIMIT: usize, 19 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 20 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 21 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 22 | const SYNC_COMMITTEE_SIZE: usize, 23 | const BYTES_PER_LOGS_BLOOM: usize, 24 | const MAX_EXTRA_DATA_BYTES: usize, 25 | >( 26 | state: &mut BeaconState< 27 | SLOTS_PER_HISTORICAL_ROOT, 28 | HISTORICAL_ROOTS_LIMIT, 29 | ETH1_DATA_VOTES_BOUND, 30 | VALIDATOR_REGISTRY_LIMIT, 31 | EPOCHS_PER_HISTORICAL_VECTOR, 32 | EPOCHS_PER_SLASHINGS_VECTOR, 33 | MAX_VALIDATORS_PER_COMMITTEE, 34 | SYNC_COMMITTEE_SIZE, 35 | BYTES_PER_LOGS_BLOOM, 36 | MAX_EXTRA_DATA_BYTES, 37 | >, 38 | context: &Context, 39 | ) -> Result<()> { 40 | let epoch = get_current_epoch(state, context); 41 | let total_balance = get_total_active_balance(state, context)?; 42 | let adjusted_total_slashing_balance = Gwei::min( 43 | state.slashings.iter().sum::() * context.proportional_slashing_multiplier_bellatrix, 44 | total_balance, 45 | ); 46 | for i in 0..state.validators.len() { 47 | let validator = &state.validators[i]; 48 | if validator.slashed && 49 | (epoch + context.epochs_per_slashings_vector / 2) == validator.withdrawable_epoch 50 | { 51 | let increment = context.effective_balance_increment; 52 | let penalty_numerator = 53 | validator.effective_balance / increment * adjusted_total_slashing_balance; 54 | let penalty = penalty_numerator / total_balance * increment; 55 | decrease_balance(state, i, penalty); 56 | } 57 | } 58 | Ok(()) 59 | } 60 | 61 | pub fn process_epoch< 62 | const SLOTS_PER_HISTORICAL_ROOT: usize, 63 | const HISTORICAL_ROOTS_LIMIT: usize, 64 | const ETH1_DATA_VOTES_BOUND: usize, 65 | const VALIDATOR_REGISTRY_LIMIT: usize, 66 | const EPOCHS_PER_HISTORICAL_VECTOR: usize, 67 | const EPOCHS_PER_SLASHINGS_VECTOR: usize, 68 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 69 | const SYNC_COMMITTEE_SIZE: usize, 70 | const BYTES_PER_LOGS_BLOOM: usize, 71 | const MAX_EXTRA_DATA_BYTES: usize, 72 | >( 73 | state: &mut BeaconState< 74 | SLOTS_PER_HISTORICAL_ROOT, 75 | HISTORICAL_ROOTS_LIMIT, 76 | ETH1_DATA_VOTES_BOUND, 77 | VALIDATOR_REGISTRY_LIMIT, 78 | EPOCHS_PER_HISTORICAL_VECTOR, 79 | EPOCHS_PER_SLASHINGS_VECTOR, 80 | MAX_VALIDATORS_PER_COMMITTEE, 81 | SYNC_COMMITTEE_SIZE, 82 | BYTES_PER_LOGS_BLOOM, 83 | MAX_EXTRA_DATA_BYTES, 84 | >, 85 | context: &Context, 86 | ) -> Result<()> { 87 | process_justification_and_finalization(state, context)?; 88 | process_inactivity_updates(state, context)?; 89 | process_rewards_and_penalties(state, context)?; 90 | process_registry_updates(state, context)?; 91 | process_slashings(state, context)?; 92 | process_eth1_data_reset(state, context); 93 | process_effective_balance_updates(state, context); 94 | process_slashings_reset(state, context); 95 | process_randao_mixes_reset(state, context); 96 | process_historical_roots_update(state, context)?; 97 | process_participation_flag_updates(state)?; 98 | process_sync_committee_updates(state, context)?; 99 | Ok(()) 100 | } 101 | -------------------------------------------------------------------------------- /ethereum-consensus/src/bellatrix/beacon_block.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | bellatrix::{ 3 | Attestation, AttesterSlashing, Deposit, Eth1Data, ExecutionPayload, ProposerSlashing, 4 | SignedVoluntaryExit, SyncAggregate, 5 | }, 6 | primitives::{BlsSignature, Bytes32, Root, Slot, ValidatorIndex}, 7 | ssz::prelude::*, 8 | }; 9 | 10 | #[derive( 11 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 12 | )] 13 | pub struct BeaconBlockBody< 14 | const MAX_PROPOSER_SLASHINGS: usize, 15 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 16 | const MAX_ATTESTER_SLASHINGS: usize, 17 | const MAX_ATTESTATIONS: usize, 18 | const MAX_DEPOSITS: usize, 19 | const MAX_VOLUNTARY_EXITS: usize, 20 | const SYNC_COMMITTEE_SIZE: usize, 21 | const BYTES_PER_LOGS_BLOOM: usize, 22 | const MAX_EXTRA_DATA_BYTES: usize, 23 | const MAX_BYTES_PER_TRANSACTION: usize, 24 | const MAX_TRANSACTIONS_PER_PAYLOAD: usize, 25 | > { 26 | pub randao_reveal: BlsSignature, 27 | pub eth1_data: Eth1Data, 28 | pub graffiti: Bytes32, 29 | pub proposer_slashings: List, 30 | pub attester_slashings: 31 | List, MAX_ATTESTER_SLASHINGS>, 32 | pub attestations: List, MAX_ATTESTATIONS>, 33 | pub deposits: List, 34 | pub voluntary_exits: List, 35 | pub sync_aggregate: SyncAggregate, 36 | pub execution_payload: ExecutionPayload< 37 | BYTES_PER_LOGS_BLOOM, 38 | MAX_EXTRA_DATA_BYTES, 39 | MAX_BYTES_PER_TRANSACTION, 40 | MAX_TRANSACTIONS_PER_PAYLOAD, 41 | >, 42 | } 43 | 44 | #[derive( 45 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 46 | )] 47 | pub struct BeaconBlock< 48 | const MAX_PROPOSER_SLASHINGS: usize, 49 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 50 | const MAX_ATTESTER_SLASHINGS: usize, 51 | const MAX_ATTESTATIONS: usize, 52 | const MAX_DEPOSITS: usize, 53 | const MAX_VOLUNTARY_EXITS: usize, 54 | const SYNC_COMMITTEE_SIZE: usize, 55 | const BYTES_PER_LOGS_BLOOM: usize, 56 | const MAX_EXTRA_DATA_BYTES: usize, 57 | const MAX_BYTES_PER_TRANSACTION: usize, 58 | const MAX_TRANSACTIONS_PER_PAYLOAD: usize, 59 | > { 60 | #[serde(with = "crate::serde::as_str")] 61 | pub slot: Slot, 62 | #[serde(with = "crate::serde::as_str")] 63 | pub proposer_index: ValidatorIndex, 64 | pub parent_root: Root, 65 | pub state_root: Root, 66 | pub body: BeaconBlockBody< 67 | MAX_PROPOSER_SLASHINGS, 68 | MAX_VALIDATORS_PER_COMMITTEE, 69 | MAX_ATTESTER_SLASHINGS, 70 | MAX_ATTESTATIONS, 71 | MAX_DEPOSITS, 72 | MAX_VOLUNTARY_EXITS, 73 | SYNC_COMMITTEE_SIZE, 74 | BYTES_PER_LOGS_BLOOM, 75 | MAX_EXTRA_DATA_BYTES, 76 | MAX_BYTES_PER_TRANSACTION, 77 | MAX_TRANSACTIONS_PER_PAYLOAD, 78 | >, 79 | } 80 | 81 | #[derive( 82 | Default, Debug, Clone, SimpleSerialize, PartialEq, Eq, serde::Serialize, serde::Deserialize, 83 | )] 84 | pub struct SignedBeaconBlock< 85 | const MAX_PROPOSER_SLASHINGS: usize, 86 | const MAX_VALIDATORS_PER_COMMITTEE: usize, 87 | const MAX_ATTESTER_SLASHINGS: usize, 88 | const MAX_ATTESTATIONS: usize, 89 | const MAX_DEPOSITS: usize, 90 | const MAX_VOLUNTARY_EXITS: usize, 91 | const SYNC_COMMITTEE_SIZE: usize, 92 | const BYTES_PER_LOGS_BLOOM: usize, 93 | const MAX_EXTRA_DATA_BYTES: usize, 94 | const MAX_BYTES_PER_TRANSACTION: usize, 95 | const MAX_TRANSACTIONS_PER_PAYLOAD: usize, 96 | > { 97 | pub message: BeaconBlock< 98 | MAX_PROPOSER_SLASHINGS, 99 | MAX_VALIDATORS_PER_COMMITTEE, 100 | MAX_ATTESTER_SLASHINGS, 101 | MAX_ATTESTATIONS, 102 | MAX_DEPOSITS, 103 | MAX_VOLUNTARY_EXITS, 104 | SYNC_COMMITTEE_SIZE, 105 | BYTES_PER_LOGS_BLOOM, 106 | MAX_EXTRA_DATA_BYTES, 107 | MAX_BYTES_PER_TRANSACTION, 108 | MAX_TRANSACTIONS_PER_PAYLOAD, 109 | >, 110 | pub signature: BlsSignature, 111 | } 112 | -------------------------------------------------------------------------------- /beacon-api-client/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod api_client; 2 | mod api_error; 3 | mod cli; 4 | mod serde; 5 | mod types; 6 | 7 | pub use api_client::*; 8 | pub use api_error::*; 9 | pub use cli::*; 10 | pub use error::*; 11 | pub use presets::*; 12 | pub use types::*; 13 | 14 | pub const ETH_CONSENSUS_VERSION_HEADER: &str = "Eth-Consensus-Version"; 15 | 16 | mod error { 17 | use crate::ApiError; 18 | use thiserror::Error; 19 | use url::ParseError; 20 | 21 | #[derive(Debug, Error)] 22 | pub enum Error { 23 | #[error("could not parse URL: {0}")] 24 | Url(#[from] ParseError), 25 | #[error("could not send request: {0}")] 26 | Http(#[from] reqwest::Error), 27 | #[error("error from API: {0}")] 28 | Api(#[from] ApiError), 29 | #[error("missing expected data in response: {0}")] 30 | MissingExpectedData(String), 31 | #[error("json error: {0}")] 32 | Json(#[from] serde_json::Error), 33 | } 34 | } 35 | 36 | pub mod presets { 37 | pub mod mainnet { 38 | use ethereum_consensus::{ 39 | altair::mainnet as altair, 40 | deneb::mainnet as deneb, 41 | phase0::mainnet as phase0, 42 | types::mainnet::{ 43 | BeaconBlock, BeaconState, BlindedBeaconBlock, SignedBeaconBlock, 44 | SignedBlindedBeaconBlock, 45 | }, 46 | }; 47 | 48 | #[derive(Clone)] 49 | pub struct MainnetClientTypes; 50 | 51 | impl crate::ClientTypes for MainnetClientTypes { 52 | type SignedContributionAndProof = altair::SignedContributionAndProof; 53 | type SyncCommitteeContribution = altair::SyncCommitteeContribution; 54 | type BlindedBeaconBlock = BlindedBeaconBlock; 55 | type SignedBlindedBeaconBlock = SignedBlindedBeaconBlock; 56 | type Attestation = phase0::Attestation; 57 | type AttesterSlashing = phase0::AttesterSlashing; 58 | type BeaconBlock = BeaconBlock; 59 | type BeaconState = BeaconState; 60 | type SignedAggregateAndProof = phase0::SignedAggregateAndProof; 61 | type SignedBeaconBlock = SignedBeaconBlock; 62 | type Blob = deneb::Blob; 63 | type BlobSidecar = deneb::BlobSidecar; 64 | type LightClientBootstrap = altair::LightClientBootstrap; 65 | type LightClientUpdate = altair::LightClientUpdate; 66 | type LightClientFinalityUpdate = altair::LightClientFinalityUpdate; 67 | type LightClientOptimisticUpdate = altair::LightClientOptimisticUpdate; 68 | } 69 | 70 | pub type Client = crate::Client; 71 | } 72 | pub mod minimal { 73 | use ethereum_consensus::{ 74 | altair::minimal as altair, 75 | deneb::minimal as deneb, 76 | phase0::minimal as phase0, 77 | types::minimal::{ 78 | BeaconBlock, BeaconState, BlindedBeaconBlock, SignedBeaconBlock, 79 | SignedBlindedBeaconBlock, 80 | }, 81 | }; 82 | 83 | #[derive(Clone)] 84 | pub struct MinimalClientTypes; 85 | 86 | impl crate::ClientTypes for MinimalClientTypes { 87 | type SignedContributionAndProof = altair::SignedContributionAndProof; 88 | type SyncCommitteeContribution = altair::SyncCommitteeContribution; 89 | type BlindedBeaconBlock = BlindedBeaconBlock; 90 | type SignedBlindedBeaconBlock = SignedBlindedBeaconBlock; 91 | type Attestation = phase0::Attestation; 92 | type AttesterSlashing = phase0::AttesterSlashing; 93 | type BeaconBlock = BeaconBlock; 94 | type BeaconState = BeaconState; 95 | type SignedAggregateAndProof = phase0::SignedAggregateAndProof; 96 | type SignedBeaconBlock = SignedBeaconBlock; 97 | type Blob = deneb::Blob; 98 | type BlobSidecar = deneb::BlobSidecar; 99 | type LightClientBootstrap = altair::LightClientBootstrap; 100 | type LightClientUpdate = altair::LightClientUpdate; 101 | type LightClientFinalityUpdate = altair::LightClientFinalityUpdate; 102 | type LightClientOptimisticUpdate = altair::LightClientOptimisticUpdate; 103 | } 104 | 105 | pub type Client = crate::Client; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /ethereum-consensus/src/configs/holesky.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | configs::Config, 3 | networks::Network, 4 | primitives::{Epoch, ExecutionAddress, Gwei, Version, FAR_FUTURE_EPOCH, U256}, 5 | }; 6 | 7 | pub const MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: usize = 16384; 8 | // Sep-28-2023 11:55:00 +UTC 9 | pub const MIN_GENESIS_TIME: u64 = 1695902100; 10 | pub const GENESIS_FORK_VERSION: Version = [1, 1, 112, 0]; 11 | pub const GENESIS_DELAY: u64 = 300; 12 | pub const SECONDS_PER_SLOT: u64 = 12; 13 | pub const SECONDS_PER_ETH1_BLOCK: u64 = 14; 14 | pub const MIN_VALIDATOR_WITHDRAWABILITY_DELAY: Epoch = 256; 15 | pub const SHARD_COMMITTEE_PERIOD: Epoch = 256; 16 | pub const ETH1_FOLLOW_DISTANCE: u64 = 2048; 17 | pub const EJECTION_BALANCE: Gwei = 28 * 10u64.pow(9); 18 | pub const MIN_PER_EPOCH_CHURN_LIMIT: u64 = 4; 19 | pub const MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: u64 = 8; 20 | pub const CHURN_LIMIT_QUOTIENT: u64 = 65536; 21 | pub const MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA: u64 = 128 * 10u64.pow(9); 22 | pub const MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT: u64 = 256 * 10u64.pow(9); 23 | pub const TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: Epoch = FAR_FUTURE_EPOCH; 24 | pub const ALTAIR_FORK_VERSION: Version = [2, 1, 112, 0]; 25 | pub const ALTAIR_FORK_EPOCH: Epoch = 0; 26 | pub const BELLATRIX_FORK_VERSION: Version = [3, 1, 112, 0]; 27 | pub const BELLATRIX_FORK_EPOCH: Epoch = 0; 28 | pub const CAPELLA_FORK_VERSION: Version = [4, 1, 112, 0]; 29 | pub const CAPELLA_FORK_EPOCH: Epoch = 256; 30 | pub const DENEB_FORK_VERSION: Version = [5, 1, 112, 0]; 31 | pub const DENEB_FORK_EPOCH: Epoch = 29696; 32 | pub const ELECTRA_FORK_VERSION: Version = [6, 1, 112, 0]; 33 | pub const ELECTRA_FORK_EPOCH: Epoch = FAR_FUTURE_EPOCH; 34 | pub const INACTIVITY_SCORE_BIAS: u64 = 4; 35 | pub const INACTIVITY_SCORE_RECOVERY_RATE: u64 = 16; 36 | pub const PROPOSER_SCORE_BOOST: u64 = 40; 37 | pub const DEPOSIT_CHAIN_ID: usize = 17000; 38 | pub const DEPOSIT_NETWORK_ID: usize = 17000; 39 | 40 | pub fn config() -> Config { 41 | let terminal_total_difficulty = U256::ZERO; 42 | let terminal_block_hash = Default::default(); 43 | let deposit_contract_address = ExecutionAddress::try_from( 44 | [ 45 | // 0x4242424242424242424242424242424242424242 46 | 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 47 | ] 48 | .as_ref(), 49 | ) 50 | .unwrap(); 51 | 52 | Config { 53 | preset_base: "mainnet".to_string(), 54 | name: Network::Holesky, 55 | terminal_total_difficulty, 56 | terminal_block_hash, 57 | terminal_block_hash_activation_epoch: TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH, 58 | min_genesis_active_validator_count: MIN_GENESIS_ACTIVE_VALIDATOR_COUNT, 59 | min_genesis_time: MIN_GENESIS_TIME, 60 | genesis_fork_version: GENESIS_FORK_VERSION, 61 | genesis_delay: GENESIS_DELAY, 62 | altair_fork_version: ALTAIR_FORK_VERSION, 63 | altair_fork_epoch: ALTAIR_FORK_EPOCH, 64 | bellatrix_fork_version: BELLATRIX_FORK_VERSION, 65 | bellatrix_fork_epoch: BELLATRIX_FORK_EPOCH, 66 | capella_fork_version: CAPELLA_FORK_VERSION, 67 | capella_fork_epoch: CAPELLA_FORK_EPOCH, 68 | deneb_fork_version: DENEB_FORK_VERSION, 69 | deneb_fork_epoch: DENEB_FORK_EPOCH, 70 | electra_fork_version: ELECTRA_FORK_VERSION, 71 | electra_fork_epoch: ELECTRA_FORK_EPOCH, 72 | seconds_per_slot: SECONDS_PER_SLOT, 73 | seconds_per_eth1_block: SECONDS_PER_ETH1_BLOCK, 74 | min_validator_withdrawability_delay: MIN_VALIDATOR_WITHDRAWABILITY_DELAY, 75 | shard_committee_period: SHARD_COMMITTEE_PERIOD, 76 | eth1_follow_distance: ETH1_FOLLOW_DISTANCE, 77 | inactivity_score_bias: INACTIVITY_SCORE_BIAS, 78 | inactivity_score_recovery_rate: INACTIVITY_SCORE_RECOVERY_RATE, 79 | ejection_balance: EJECTION_BALANCE, 80 | min_per_epoch_churn_limit: MIN_PER_EPOCH_CHURN_LIMIT, 81 | max_per_epoch_activation_churn_limit: MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT, 82 | min_per_epoch_churn_limit_electra: MIN_PER_EPOCH_CHURN_LIMIT_ELECTRA, 83 | max_per_epoch_activation_exit_churn_limit: MAX_PER_EPOCH_ACTIVATION_EXIT_CHURN_LIMIT, 84 | churn_limit_quotient: CHURN_LIMIT_QUOTIENT, 85 | proposer_score_boost: PROPOSER_SCORE_BOOST, 86 | deposit_chain_id: DEPOSIT_CHAIN_ID, 87 | deposit_network_id: DEPOSIT_NETWORK_ID, 88 | deposit_contract_address, 89 | } 90 | } 91 | --------------------------------------------------------------------------------