diff --git a/Cargo.toml b/Cargo.toml index bdd14a0..0faf803 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,9 +67,11 @@ snark-verifier-sdk = { git = "https://github.com/axiom-crypto/snark-verifier.git ] } # ethereum types -ethereum-consensus-types = { git = "https://github.com/ChainSafe/ethereum-consensus-types", branch = "capella" } -beacon-api-client = { git = "https://github.com/ralexstokes/ethereum-consensus.git", rev = "f3bff52e9c43866f231ec40c8ab0e34125a8957f" } -ssz_rs = "0.9" +ethereum-types = { package = "types", git = "https://github.com/sigp/lighthouse/", tag = "v5.1.3", default-features = false, features = ["legacy-arith"] } +tree_hash = { version = "0.5" } +eth2 = { package = "eth2", git = "https://github.com/sigp/lighthouse/", tag = "v5.1.3" } +merkle_proof = { package = "merkle_proof", git = "https://github.com/sigp/lighthouse/", tag = "v5.1.3" } +ethereum_ssz = "0.5.3" # crypto group = "0.13" @@ -90,7 +92,6 @@ ark-std = { version = "0.4.0", features = ["print-trace"] } [patch.crates-io] -ssz_rs = { git = "https://github.com/ralexstokes/ssz-rs", rev = "5f1ec833718efa07bbbff427ab28a1eeaa706164" } halo2-base = { git = "https://github.com/nulltea/halo2-lib", branch = "feat/bls12-381-hash2curve" } halo2-ecc = { git = "https://github.com/nulltea/halo2-lib", branch = "feat/bls12-381-hash2curve" } zkevm-hashes = { git = "https://github.com/nulltea/halo2-lib", branch = "feat/bls12-381-hash2curve" } diff --git a/contract-tests/Cargo.toml b/contract-tests/Cargo.toml index 61800d7..6138c95 100644 --- a/contract-tests/Cargo.toml +++ b/contract-tests/Cargo.toml @@ -16,7 +16,8 @@ itertools = "0.11.0" test-utils = { workspace = true } halo2curves = { workspace = true } eth-types = { workspace = true } -ssz_rs = { workspace = true } halo2-base = { workspace = true } snark-verifier-sdk = { workspace = true } contracts = { workspace = true } + +tree_hash.workspace = true \ No newline at end of file diff --git a/contract-tests/tests/step_input_encoding.rs b/contract-tests/tests/step_input_encoding.rs index cf2c2e6..b6ae700 100644 --- a/contract-tests/tests/step_input_encoding.rs +++ b/contract-tests/tests/step_input_encoding.rs @@ -2,7 +2,6 @@ // Code: https://github.com/ChainSafe/Spectre // SPDX-License-Identifier: LGPL-3.0-only -use std::ops::Deref; use std::path::PathBuf; use contract_tests::make_client; @@ -11,8 +10,8 @@ use ethers::contract::abigen; use lightclient_circuits::halo2_proofs::halo2curves::bn256; use lightclient_circuits::witness::SyncStepArgs; use rstest::rstest; -use ssz_rs::Merkleized; use test_utils::read_test_files_and_gen_witness; +use tree_hash::TreeHash; abigen!( StepExternal, @@ -28,20 +27,13 @@ impl From> for StepInput { .map(|v| *v as u64) .sum::(); - let finalized_header_root: [u8; 32] = args - .finalized_header - .clone() - .hash_tree_root() - .unwrap() - .deref() - .try_into() - .unwrap(); + let finalized_header_root: [u8; 32] = args.finalized_header.clone().tree_hash_root().0; let execution_payload_root: [u8; 32] = args.execution_payload_root.try_into().unwrap(); StepInput { - attested_slot: args.attested_header.slot, - finalized_slot: args.finalized_header.slot, + attested_slot: args.attested_header.slot.into(), + finalized_slot: args.finalized_header.slot.into(), participation, finalized_header_root, execution_payload_root, diff --git a/contracts b/contracts index a430cae..a5d8d27 160000 --- a/contracts +++ b/contracts @@ -1 +1 @@ -Subproject commit a430caeb3678582b73e3ee73b6f001bd9d1e75ca +Subproject commit a5d8d270ffa0d67e92770a7bcaa0acdfff76633b diff --git a/eth-types/Cargo.toml b/eth-types/Cargo.toml index 996b84d..200d2a7 100644 --- a/eth-types/Cargo.toml +++ b/eth-types/Cargo.toml @@ -10,15 +10,13 @@ pasta_curves.workspace = true uint.workspace = true hex.workspace = true halo2-base.workspace = true -halo2-ecc.workspace = true serde.workspace = true serde_json.workspace = true -itertools.workspace = true halo2curves.workspace = true +ethereum-types.workspace = true regex = "1.5.4" lazy_static = "1.4" subtle = "2.4" -num = "0.4" num-bigint.workspace = true strum_macros = "0.24" strum = "0.24" diff --git a/eth-types/src/spec.rs b/eth-types/src/spec.rs index ba05992..82802fa 100644 --- a/eth-types/src/spec.rs +++ b/eth-types/src/spec.rs @@ -3,9 +3,11 @@ // SPDX-License-Identifier: LGPL-3.0-only use core::fmt::Debug; - +use ethereum_types::{EthSpec, MainnetEthSpec, MinimalEthSpec}; /// Beacon chain specification. pub trait Spec: 'static + Sized + Copy + Default + Debug { + type EthSpec: EthSpec; + const NAME: &'static str; const SYNC_COMMITTEE_SIZE: usize; const SYNC_COMMITTEE_ROOT_INDEX: usize; @@ -18,14 +20,14 @@ pub trait Spec: 'static + Sized + Copy + Default + Debug { const EXECUTION_STATE_ROOT_DEPTH: usize; const FINALIZED_HEADER_INDEX: usize; const FINALIZED_HEADER_DEPTH: usize; - const BYTES_PER_LOGS_BLOOM: usize = 256; - const MAX_EXTRA_DATA_BYTES: usize = 32; } #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] pub struct Minimal; impl Spec for Minimal { + type EthSpec = MinimalEthSpec; + const NAME: &'static str = "minimal"; const SYNC_COMMITTEE_SIZE: usize = 32; const SYNC_COMMITTEE_DEPTH: usize = 5; @@ -38,15 +40,14 @@ impl Spec for Minimal { const EXECUTION_STATE_ROOT_DEPTH: usize = 4; const FINALIZED_HEADER_INDEX: usize = 105; const FINALIZED_HEADER_DEPTH: usize = 6; - - const BYTES_PER_LOGS_BLOOM: usize = 256; - const MAX_EXTRA_DATA_BYTES: usize = 32; } #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] pub struct Testnet; impl Spec for Testnet { + type EthSpec = MainnetEthSpec; + const NAME: &'static str = "testnet"; const SYNC_COMMITTEE_SIZE: usize = 512; const SYNC_COMMITTEE_DEPTH: usize = 5; @@ -58,15 +59,14 @@ impl Spec for Testnet { const EXECUTION_STATE_ROOT_DEPTH: usize = 4; const FINALIZED_HEADER_INDEX: usize = 105; const FINALIZED_HEADER_DEPTH: usize = 6; - - const BYTES_PER_LOGS_BLOOM: usize = 256; - const MAX_EXTRA_DATA_BYTES: usize = 32; } #[derive(Copy, Clone, PartialEq, Eq, Debug, Default)] pub struct Mainnet; impl Spec for Mainnet { + type EthSpec = MainnetEthSpec; + const NAME: &'static str = "mainnet"; const SYNC_COMMITTEE_SIZE: usize = 512; const SYNC_COMMITTEE_DEPTH: usize = 5; @@ -78,7 +78,4 @@ impl Spec for Mainnet { const EXECUTION_STATE_ROOT_DEPTH: usize = 4; const FINALIZED_HEADER_INDEX: usize = 105; const FINALIZED_HEADER_DEPTH: usize = 6; - - const BYTES_PER_LOGS_BLOOM: usize = 256; - const MAX_EXTRA_DATA_BYTES: usize = 32; } diff --git a/lightclient-circuits/Cargo.toml b/lightclient-circuits/Cargo.toml index fa18d72..ffd5de2 100644 --- a/lightclient-circuits/Cargo.toml +++ b/lightclient-circuits/Cargo.toml @@ -25,14 +25,13 @@ sha2.workspace = true pse-poseidon = { git = "https://github.com/axiom-crypto/pse-poseidon.git" } # ethereum -ssz_rs = { workspace = true, features = ["serde"] } -ethereum-consensus-types ={ workspace = true, features = ["serde"] } +ethereum-types = {workspace = true, default-features = false } +tree_hash.workspace = true # local eth-types.workspace = true # misc -ark-std.workspace = true serde.workspace = true serde_json.workspace = true itertools.workspace = true @@ -40,14 +39,13 @@ log.workspace = true hex.workspace = true rayon = "1.7.0" array-init = "2.0.0" -strum = "0.25" -strum_macros = "0.25" rand = "0.8" lazy_static = "1.4" getset = "0.1.2" rand_chacha = "0.3.0" [dev-dependencies] +ark-std.workspace = true rstest = "0.18.2" test-utils = { workspace = true } diff --git a/lightclient-circuits/src/committee_update_circuit.rs b/lightclient-circuits/src/committee_update_circuit.rs index 31ed653..15bf7e7 100644 --- a/lightclient-circuits/src/committee_update_circuit.rs +++ b/lightclient-circuits/src/committee_update_circuit.rs @@ -29,8 +29,8 @@ use halo2_ecc::{ fields::FieldChip, }; use itertools::Itertools; -use ssz_rs::Merkleized; use std::{env::var, iter, marker::PhantomData, vec}; +use tree_hash::TreeHash; /// `CommitteeUpdateCircuit` maps next sync committee SSZ root in the finalized state root to the corresponding Poseidon commitment to the public keys. /// @@ -90,7 +90,7 @@ impl CommitteeUpdateCircuit { builder, &sha256_chip, [ - args.finalized_header.slot.into_witness(), + args.finalized_header.slot.as_u64().into_witness(), args.finalized_header.proposer_index.into_witness(), args.finalized_header.parent_root.as_ref().into_witness(), finalized_state_root.clone().into(), @@ -198,14 +198,11 @@ impl CommitteeUpdateCircuit { pub fn get_instances( args: &witness::CommitteeUpdateArgs, limb_bits: usize, - ) -> Vec> - where - [(); S::SYNC_COMMITTEE_SIZE]:, - { + ) -> Vec> { let poseidon_commitment = poseidon_committee_commitment_from_compressed(&args.pubkeys_compressed, limb_bits); - let finalized_header_root = args.finalized_header.clone().hash_tree_root().unwrap(); + let finalized_header_root = args.finalized_header.tree_hash_root(); let finalized_header_root_hilo = { let bytes = finalized_header_root.as_ref(); @@ -279,12 +276,7 @@ mod tests { use ark_std::{end_timer, start_timer}; use eth_types::Testnet; use halo2_base::{ - halo2_proofs::{ - dev::MockProver, - halo2curves::bn256::Fr, - plonk::ProvingKey, - poly::{commitment::Params, kzg::commitment::ParamsKZG}, - }, + halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr, plonk::ProvingKey}, utils::fs::gen_srs, }; use snark_verifier_sdk::evm::{evm_verify, gen_evm_proof_shplonk}; diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index f85d4ec..f6d6d78 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -42,8 +42,8 @@ use halo2_ecc::{ use halo2curves::bls12_381::{G1Affine, G2Affine}; use itertools::Itertools; use num_bigint::BigUint; -use ssz_rs::Merkleized; use std::{env::var, marker::PhantomData, vec}; +use tree_hash::TreeHash; /// `StepCircuit` verifies that Beacon chain block header is attested by a lightclient sync committee via aggregated signature, /// and the execution (Eth1) payload via Merkle proof against the finalized block header. @@ -110,7 +110,8 @@ impl StepCircuit { )?; // Compute attested header root - let attested_slot_bytes: HashInputChunk<_> = args.attested_header.slot.into_witness(); + let attested_slot_bytes: HashInputChunk<_> = + args.attested_header.slot.as_u64().into_witness(); let attested_header_state_root = args .attested_header .state_root @@ -138,7 +139,8 @@ impl StepCircuit { .iter() .map(|&b| builder.main().load_witness(F::from(b as u64))) .collect_vec(); - let finalized_slot_bytes: HashInputChunk<_> = args.finalized_header.slot.into_witness(); + let finalized_slot_bytes: HashInputChunk<_> = + args.finalized_header.slot.as_u64().into_witness(); let finalized_header_root = ssz_merkleize_chunks( builder, &sha256_chip, @@ -230,11 +232,11 @@ impl StepCircuit { const INPUT_SIZE: usize = 8 * 3 + 32 * 2; let mut input = [0; INPUT_SIZE]; - let mut attested_slot_le = args.attested_header.slot.to_le_bytes().to_vec(); + let mut attested_slot_le = args.attested_header.slot.as_u64().to_le_bytes().to_vec(); attested_slot_le.resize(8, 0); input[..8].copy_from_slice(&attested_slot_le); - let mut finalized_slot_le = args.finalized_header.slot.to_le_bytes().to_vec(); + let mut finalized_slot_le = args.finalized_header.slot.as_u64().to_le_bytes().to_vec(); finalized_slot_le.resize(8, 0); input[8..16].copy_from_slice(&finalized_slot_le); @@ -250,9 +252,7 @@ impl StepCircuit { let finalized_header_root: [u8; 32] = args .finalized_header - .clone() - .hash_tree_root() - .unwrap() + .tree_hash_root() .as_ref() .try_into() .unwrap(); @@ -435,10 +435,7 @@ impl AppCircuit for StepCircuit { mod tests { use std::fs; - use crate::{ - aggregation_circuit::AggregationConfigPinning, util::Halo2ConfigPinning, - witness::SyncStepArgs, - }; + use crate::{aggregation_circuit::AggregationConfigPinning, util::Halo2ConfigPinning}; use super::*; use ark_std::{end_timer, start_timer}; diff --git a/lightclient-circuits/src/witness/multiproof.rs b/lightclient-circuits/src/witness/multiproof.rs index 59ec59f..44ff695 100644 --- a/lightclient-circuits/src/witness/multiproof.rs +++ b/lightclient-circuits/src/witness/multiproof.rs @@ -1,8 +1,8 @@ // TODO: A lot if not all/most of this code is copy pasta from: https://github.com/ralexstokes/ssz-rs/pull/118 which is mostly implemented w.r.t. the spec // TODO: Remove this once the above PR lands in ssz-rs +use ethereum_types::Hash256; use sha2::{Digest, Sha256}; -use ssz_rs::Node; use std::collections::{HashMap, HashSet}; pub type GeneralizedIndex = usize; @@ -93,7 +93,7 @@ pub fn get_helper_indices(indices: &[GeneralizedIndex]) -> Vec all_branch_indices } -pub fn calculate_merkle_root(leaf: Node, proof: &[Node], index: GeneralizedIndex) -> Node { +pub fn calculate_merkle_root(leaf: Hash256, proof: &[Hash256], index: GeneralizedIndex) -> Hash256 { debug_assert_eq!(proof.len(), get_path_length(index)); let mut result = leaf; @@ -114,15 +114,15 @@ pub fn calculate_merkle_root(leaf: Node, proof: &[Node], index: GeneralizedIndex /// Calculate the Merkle root of a set of leaves and their corresponding proofs. /// Note: `indices` and `leaves` must be in the same order as they correspond to each other. pub fn calculate_multi_merkle_root( - leaves: &[Node], - proof: &[Node], + leaves: &[Hash256], + proof: &[Hash256], indices: &[GeneralizedIndex], -) -> Node { +) -> Hash256 { assert_eq!(leaves.len(), indices.len()); let helper_indices = get_helper_indices(indices); assert_eq!(proof.len(), helper_indices.len()); - let mut objects: HashMap = indices + let mut objects: HashMap = indices .iter() .chain(helper_indices.iter()) .copied() @@ -163,9 +163,9 @@ pub fn calculate_multi_merkle_root( /// Return an array representing the tree nodes by generalized index: /// [0, 1, 2, 3, 4, 5, 6, 7], where each layer is a power of 2. The 0 index is ignored. The 1 index is the root. /// The result will be twice the size as the padded bottom layer for the input leaves. -pub fn merkle_tree(leaves: &[Node]) -> Vec { +pub fn merkle_tree(leaves: &[Hash256]) -> Vec { let bottom_length = get_power_of_two_ceil(leaves.len()); - let mut o = vec![Node::default(); bottom_length * 2]; + let mut o = vec![Hash256::default(); bottom_length * 2]; o[bottom_length..bottom_length + leaves.len()].copy_from_slice(leaves); for i in (1..bottom_length).rev() { let left = o[i * 2].as_ref(); @@ -178,7 +178,10 @@ pub fn merkle_tree(leaves: &[Node]) -> Vec { o } -pub fn create_multiproof(merkle_tree: &[Node], indices_to_prove: &[GeneralizedIndex]) -> Vec { +pub fn create_multiproof( + merkle_tree: &[Hash256], + indices_to_prove: &[GeneralizedIndex], +) -> Vec { get_helper_indices(indices_to_prove) .into_iter() .map(|i| merkle_tree[i]) diff --git a/lightclient-circuits/src/witness/rotation.rs b/lightclient-circuits/src/witness/rotation.rs index 8539b46..b12681c 100644 --- a/lightclient-circuits/src/witness/rotation.rs +++ b/lightclient-circuits/src/witness/rotation.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: LGPL-3.0-only use eth_types::Spec; -use ethereum_consensus_types::BeaconBlockHeader; +use ethereum_types::BeaconBlockHeader; use itertools::Itertools; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -60,21 +60,21 @@ impl Default for CommitteeUpdateArgs { S::SYNC_COMMITTEE_PUBKEYS_ROOT_INDEX, ); + let mut finalized_header = BeaconBlockHeader::empty(); + finalized_header.state_root = state_root.into(); + Self { pubkeys_compressed: iter::repeat(dummy_x_bytes) .take(S::SYNC_COMMITTEE_SIZE) .collect_vec(), sync_committee_branch, - finalized_header: BeaconBlockHeader { - state_root: state_root.as_slice().try_into().unwrap(), - ..Default::default() - }, + finalized_header, _spec: PhantomData, } } } -pub(crate) fn mock_root(leaf: Vec, branch: &[Vec], mut gindex: usize) -> Vec { +pub(crate) fn mock_root(leaf: Vec, branch: &[Vec], mut gindex: usize) -> [u8; 32] { let mut last_hash = leaf; for i in 0..branch.len() { @@ -90,7 +90,7 @@ pub(crate) fn mock_root(leaf: Vec, branch: &[Vec], mut gindex: usize) -> gindex /= 2; } - last_hash + last_hash.try_into().unwrap() } #[cfg(test)] diff --git a/lightclient-circuits/src/witness/step.rs b/lightclient-circuits/src/witness/step.rs index 865a836..f0ebd0e 100644 --- a/lightclient-circuits/src/witness/step.rs +++ b/lightclient-circuits/src/witness/step.rs @@ -3,8 +3,7 @@ // SPDX-License-Identifier: LGPL-3.0-only use eth_types::Spec; -use ethereum_consensus_types::signing::compute_signing_root; -use ethereum_consensus_types::BeaconBlockHeader; +use ethereum_types::{BeaconBlockHeader, SignedRoot}; use ff::Field; use halo2curves::bls12_381::hash_to_curve::ExpandMsgXmd; use halo2curves::bls12_381::{hash_to_curve, Fr, G1, G2}; @@ -12,9 +11,8 @@ use halo2curves::group::Curve; use itertools::Itertools; use rand::SeedableRng; use serde::{Deserialize, Serialize}; -use ssz_rs::{Merkleized, Node}; use std::marker::PhantomData; -use std::ops::Deref; +use tree_hash::TreeHash; use super::mock_root; @@ -64,28 +62,23 @@ impl Default for SyncStepArgs { S::EXECUTION_STATE_ROOT_INDEX, ); - let mut finalized_header = BeaconBlockHeader { - body_root: Node::try_from(beacon_block_body_root.as_slice()).unwrap(), - ..Default::default() - }; + let mut finalized_header = BeaconBlockHeader::empty(); + finalized_header.body_root = beacon_block_body_root.into(); - let finality_header_root = finalized_header.hash_tree_root().unwrap(); + let finality_header_root = finalized_header.tree_hash_root(); let finality_branch = vec![vec![0; 32]; S::FINALIZED_HEADER_DEPTH]; let attested_state_root = mock_root( - finality_header_root.deref().to_vec(), + finality_header_root.0.to_vec(), &finality_branch, S::FINALIZED_HEADER_INDEX, ); - let mut attested_header = BeaconBlockHeader { - state_root: Node::try_from(attested_state_root.as_slice()).unwrap(), - ..Default::default() - }; + let mut attested_header = BeaconBlockHeader::empty(); + attested_header.state_root = attested_state_root.into(); - let signing_root = - compute_signing_root(attested_header.hash_tree_root().unwrap(), DOMAIN).unwrap(); + let signing_root = attested_header.tree_hash_root().signing_root(DOMAIN.into()); let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0); @@ -93,7 +86,7 @@ impl Default for SyncStepArgs { .map(|_| Fr::random(&mut rng)) .collect_vec(); let msg = >>::hash_to_curve( - signing_root.deref(), + signing_root.0, S::DST, ) .to_affine(); diff --git a/preprocessor/Cargo.toml b/preprocessor/Cargo.toml index aa7679a..71c8607 100644 --- a/preprocessor/Cargo.toml +++ b/preprocessor/Cargo.toml @@ -14,29 +14,31 @@ required-features = ["test-gen"] [dependencies] -ssz_rs.workspace = true halo2curves.workspace = true group.workspace = true -beacon-api-client.workspace = true # misc eyre = "0.6" tokio = { version = "1", features = ["full"] } +url = "2.2.2" hex.workspace = true log.workspace = true itertools.workspace = true serde_json.workspace = true serde.workspace = true -ethereum-consensus-types.workspace = true + +ethereum-types = {workspace = true, default-features = false } +eth2.workspace = true +tree_hash.workspace = true +merkle_proof.workspace = true + # local eth-types.workspace = true lightclient-circuits.workspace = true reqwest = "0.11.22" -# for test gen binary -ethereum-consensus = { git = "https://github.com/ralexstokes/ethereum-consensus.git", rev = "f3bff52e9c43866f231ec40c8ab0e34125a8957f", optional = true } -blst = { version = "0.3.11", default-features = false, features = [ "portable", "no-threads" ], optional = true } +blst = { version = "0.3.11", default-features = false, features = [ "portable", "no-threads" ]} [dev-dependencies] halo2-base.workspace = true @@ -44,4 +46,4 @@ snark-verifier-sdk.workspace = true ark-std.workspace = true [features] -test-gen = ["ethereum-consensus", "blst"] \ No newline at end of file +test-gen = [] \ No newline at end of file diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs index cfe6302..402de3b 100644 --- a/preprocessor/src/lib.rs +++ b/preprocessor/src/lib.rs @@ -7,159 +7,75 @@ mod rotation; mod step; - -use beacon_api_client::{BlockId, Client, ClientTypes, Value, VersionedValue}; +use eth2::mixin::RequestAccept as _; +use eth2::types::Accept; +use eth2::BeaconNodeHttpClient; use eth_types::Spec; -use ethereum_consensus_types::bls::BlsSignature; -use ethereum_consensus_types::{ - BeaconBlockHeader, BlsPublicKey, ByteVector, LightClientBootstrap, LightClientFinalityUpdate, - LightClientUpdateCapella, Root, +use ethereum_types::{ + EthSpec, FixedVector, LightClientFinalityUpdate, LightClientFinalityUpdateCapella, + LightClientFinalityUpdateDeneb, PublicKeyBytes, }; - -use itertools::Itertools; +use ethereum_types::{ForkVersionedResponse, LightClientUpdate}; use lightclient_circuits::witness::{CommitteeUpdateArgs, SyncStepArgs}; pub use rotation::*; -use serde::{Deserialize, Serialize}; -use ssz_rs::{Node, Vector}; pub use step::*; +use url::Url; -pub async fn get_light_client_update_at_period( - client: &Client, +pub async fn get_light_client_update_at_period( + client: &BeaconNodeHttpClient, period: u64, -) -> eyre::Result< - LightClientUpdateCapella< - { S::SYNC_COMMITTEE_SIZE }, - { S::SYNC_COMMITTEE_ROOT_INDEX }, - { S::SYNC_COMMITTEE_DEPTH }, - { S::FINALIZED_HEADER_INDEX }, - { S::FINALIZED_HEADER_DEPTH }, - { S::BYTES_PER_LOGS_BLOOM }, - { S::MAX_EXTRA_DATA_BYTES }, - >, -> -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, - [(); S::FINALIZED_HEADER_INDEX]:, -{ - let route = "eth/v1/beacon/light_client/updates"; - let mut updates: Vec> = client - .http - .get(client.endpoint.join(route)?) - .query(&[("start_period", period), ("count", 1)]) - .send() - .await? - .json() - .await?; +) -> eyre::Result> { + let mut path = Url::parse(client.as_ref()).map_err(|e| eyre::eyre!("Invalid URL: {:?}", e))?; + + path.path_segments_mut() + .map_err(|()| eyre::eyre!("Invalid URL: {}", client.as_ref()))? + .push("eth") + .push("v1") + .push("beacon") + .push("light_client") + .push("updates"); + + path.query_pairs_mut() + .append_pair("start_period", &period.to_string()) + .append_pair("count", "1"); + let resp = client + .get_response(path, |b| b.accept(Accept::Json)) + .await + .map_err(|e| eyre::eyre!("Failed to get light client update: {:?}", e))?; + + let mut updates: Vec>> = + resp.json().await?; + assert!(updates.len() == 1, "should only get one update"); Ok(updates.pop().unwrap().data) } -pub async fn get_light_client_bootstrap( - client: &Client, - block_root: Node, -) -> eyre::Result< - LightClientBootstrap< - { S::SYNC_COMMITTEE_SIZE }, - { S::SYNC_COMMITTEE_DEPTH }, - { S::BYTES_PER_LOGS_BLOOM }, - { S::MAX_EXTRA_DATA_BYTES }, - >, -> -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, -{ - let route = format!("eth/v1/beacon/light_client/bootstrap/{block_root:?}"); - let bootstrap = client.get::>(&route).await?.data; - Ok(bootstrap) -} - -pub async fn get_light_client_finality_update( - client: &Client, -) -> eyre::Result< - LightClientFinalityUpdate< - { S::SYNC_COMMITTEE_SIZE }, - { S::FINALIZED_HEADER_DEPTH }, - { S::BYTES_PER_LOGS_BLOOM }, - { S::MAX_EXTRA_DATA_BYTES }, - >, -> -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::FINALIZED_HEADER_DEPTH]:, -{ - Ok(client - .get::>("eth/v1/beacon/light_client/finality_update") - .await? - .data) -} - -pub async fn get_block_header( - client: &Client, - id: BlockId, -) -> eyre::Result { - // TODO: Once the ethereum beacon_api_client is updated, we can avoid this struct definition - #[derive(Serialize, Deserialize)] - struct BeaconHeaderSummary { - pub root: Root, - pub canonical: bool, - pub header: SignedBeaconBlockHeader, - } - #[derive(Serialize, Deserialize)] - struct SignedBeaconBlockHeader { - pub message: BeaconBlockHeader, - pub signature: BlsSignature, - } - - let route = format!("eth/v1/beacon/headers/{id}"); - let block: BeaconHeaderSummary = client.get::>(&route).await?.data; - Ok(block.header.message) -} - pub async fn light_client_update_to_args( - update: &LightClientUpdateCapella< - { S::SYNC_COMMITTEE_SIZE }, - { S::SYNC_COMMITTEE_ROOT_INDEX }, - { S::SYNC_COMMITTEE_DEPTH }, - { S::FINALIZED_HEADER_INDEX }, - { S::FINALIZED_HEADER_DEPTH }, - { S::BYTES_PER_LOGS_BLOOM }, - { S::MAX_EXTRA_DATA_BYTES }, - >, - pubkeys_compressed: Vector, + update: &LightClientUpdate, + pubkeys_compressed: &FixedVector::SyncCommitteeSize>, domain: [u8; 32], -) -> eyre::Result<(SyncStepArgs, CommitteeUpdateArgs)> -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, - [(); S::FINALIZED_HEADER_INDEX]:, -{ - let finality_update = LightClientFinalityUpdate { - attested_header: update.attested_header.clone(), - finalized_header: update.finalized_header.clone(), - finality_branch: Vector::try_from( - update - .finality_branch - .iter() - .map(|v| ByteVector(Vector::try_from(v.to_vec()).unwrap())) - .collect_vec(), - ) - .unwrap(), - sync_aggregate: update.sync_aggregate.clone(), - signature_slot: update.signature_slot, +) -> eyre::Result<(SyncStepArgs, CommitteeUpdateArgs)> { + let finality_update = match update { + LightClientUpdate::Altair(_) => unimplemented!(), + LightClientUpdate::Capella(update) => { + LightClientFinalityUpdate::Capella(LightClientFinalityUpdateCapella { + attested_header: update.attested_header.clone(), + finalized_header: update.finalized_header.clone(), + finality_branch: update.finality_branch.clone(), + sync_aggregate: update.sync_aggregate.clone(), + signature_slot: update.signature_slot, + }) + } + + LightClientUpdate::Deneb(update) => { + LightClientFinalityUpdate::Deneb(LightClientFinalityUpdateDeneb { + attested_header: update.attested_header.clone(), + finalized_header: update.finalized_header.clone(), + finality_branch: update.finality_branch.clone(), + sync_aggregate: update.sync_aggregate.clone(), + signature_slot: update.signature_slot, + }) + } }; let rotation_args = rotation::rotation_args_from_update(update).await?; @@ -172,13 +88,12 @@ where #[cfg(test)] mod tests { - use beacon_api_client::StateId; + use eth2::types::{BlockId, StateId}; use eth_types::Testnet; - use ethereum_consensus_types::signing::{compute_domain, DomainType}; - use ethereum_consensus_types::ForkData; use halo2_base::halo2_proofs::halo2curves::bn256::Bn256; use halo2_base::halo2_proofs::poly::kzg::commitment::ParamsKZG; use halo2_base::utils::fs::gen_srs; + use itertools::Itertools; use lightclient_circuits::committee_update_circuit::CommitteeUpdateCircuit; use lightclient_circuits::halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; use lightclient_circuits::util::{Eth2ConfigPinning, Halo2ConfigPinning}; @@ -189,16 +104,29 @@ mod tests { use snark_verifier_sdk::CircuitExt; use super::*; - use beacon_api_client::mainnet::Client as MainnetClient; - use reqwest::Url; + + use eth2::{SensitiveUrl, Timeouts}; + use ethereum_types::Domain; + use ethereum_types::MainnetEthSpec; + use std::time::Duration; #[tokio::test] async fn test_both_circuit_sepolia() { const K: u32 = 21; - let client = - MainnetClient::new(Url::parse("https://lodestar-sepolia.chainsafe.io").unwrap()); + const URL: &str = "https://lodestar-sepolia.chainsafe.io"; + let client = BeaconNodeHttpClient::new( + SensitiveUrl::parse(URL).unwrap(), + Timeouts::set_all(Duration::from_secs(10)), + ); - let block = get_block_header(&client, BlockId::Finalized).await.unwrap(); + let block = client + .get_beacon_headers_block_id(BlockId::Finalized) + .await + .unwrap() + .unwrap() + .data + .header + .message; let slot = block.slot; let period = slot / (32 * 256); @@ -209,59 +137,66 @@ mod tests { // Fetch light client update and create circuit arguments let (s, mut c) = { - let update = get_light_client_update_at_period(&client, period) + let update = get_light_client_update_at_period::(&client, period.into()) .await .unwrap(); - let block_root = client - .get_beacon_block_root(BlockId::Slot(slot)) - .await - .unwrap(); + let block_root = block.canonical_root(); - let bootstrap = get_light_client_bootstrap(&client, block_root) + let bootstrap = client + .get_light_client_bootstrap::(block_root) .await - .unwrap(); + .unwrap() + .unwrap() + .data; - let pubkeys_compressed = bootstrap.current_sync_committee.pubkeys; + let pubkeys_compressed = &bootstrap.current_sync_committee().pubkeys; let fork_version = client - .get_fork(StateId::Head) + .get_beacon_states_fork(StateId::Head) .await .unwrap() + .unwrap() + .data .current_version; + let genesis_validators_root = client - .get_genesis_details() + .get_beacon_genesis() .await .unwrap() + .data .genesis_validators_root; - let fork_data = ForkData { - genesis_validators_root, + + let domain = MainnetEthSpec::default_spec().compute_domain( + Domain::SyncCommittee, fork_version, - }; - let domain = compute_domain(DomainType::SyncCommittee, &fork_data).unwrap(); - light_client_update_to_args::(&update, pubkeys_compressed, domain) + genesis_validators_root, + ); + + light_client_update_to_args::(&update, pubkeys_compressed, domain.into()) .await .unwrap() }; let mut finalized_sync_committee_branch = { - let block_root = client - .get_beacon_block_root(BlockId::Slot(s.finalized_header.slot)) - .await - .unwrap(); + let block_root = s.finalized_header.canonical_root(); - get_light_client_bootstrap::(&client, block_root) + let k = client + .get_light_client_bootstrap::(block_root) .await .unwrap() - .current_sync_committee_branch - .iter() - .map(|n| n.to_vec()) - .collect_vec() + .unwrap() + .data + .current_sync_committee_branch() + .into_iter() + .map(|n| n.0.to_vec()) + .collect_vec(); + k }; // Magic swap of sync committee branch finalized_sync_committee_branch.insert(0, c.sync_committee_branch[0].clone()); - finalized_sync_committee_branch[1] = c.sync_committee_branch[1].clone(); + finalized_sync_committee_branch[1].clone_from(&c.sync_committee_branch[1]); c.sync_committee_branch = finalized_sync_committee_branch; // Replaces the attested header with step circuits finalized header c.finalized_header = s.finalized_header.clone(); diff --git a/preprocessor/src/rotation.rs b/preprocessor/src/rotation.rs index 8c1b146..bdbef31 100644 --- a/preprocessor/src/rotation.rs +++ b/preprocessor/src/rotation.rs @@ -4,101 +4,84 @@ use std::marker::PhantomData; -use beacon_api_client::{BlockId, Client, ClientTypes}; use eth_types::Spec; -use ethereum_consensus_types::LightClientUpdateCapella; use itertools::Itertools; -use lightclient_circuits::witness::CommitteeUpdateArgs; -use log::debug; -use ssz_rs::Merkleized; -use crate::{get_block_header, get_light_client_update_at_period}; +use crate::get_light_client_update_at_period; +use eth2::{types::BlockId, BeaconNodeHttpClient}; +use ethereum_types::LightClientUpdate; +use lightclient_circuits::witness::CommitteeUpdateArgs; +use tree_hash::TreeHash; /// Fetches LightClientUpdate from the beacon client and converts it to a [`CommitteeUpdateArgs`] witness -pub async fn fetch_rotation_args( - client: &Client, -) -> eyre::Result> -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, - [(); S::FINALIZED_HEADER_INDEX]:, -{ - let block = get_block_header(client, BlockId::Head).await?; - let slot = block.slot; +pub async fn fetch_rotation_args( + client: &BeaconNodeHttpClient, +) -> eyre::Result> { + let block = client + .get_beacon_headers_block_id(BlockId::Finalized) + .await + .map_err(|e| eyre::eyre!("Failed to get block id: {:?}", e))? + .ok_or(eyre::eyre!("Failed to get block id: None"))? + .data + .header + .message; + + let slot = block.slot.as_u64(); let period = slot / (32 * 256); - debug!( + println!( "Fetching light client update at current Slot: {} at Period: {}", slot, period ); - let update = get_light_client_update_at_period(client, period).await?; + let update = get_light_client_update_at_period::(client, period).await?; rotation_args_from_update(&update).await } /// Converts a [`LightClientUpdateCapella`] to a [`CommitteeUpdateArgs`] witness. pub async fn rotation_args_from_update( - update: &LightClientUpdateCapella< - { S::SYNC_COMMITTEE_SIZE }, - { S::SYNC_COMMITTEE_ROOT_INDEX }, - { S::SYNC_COMMITTEE_DEPTH }, - { S::FINALIZED_HEADER_INDEX }, - { S::FINALIZED_HEADER_DEPTH }, - { S::BYTES_PER_LOGS_BLOOM }, - { S::MAX_EXTRA_DATA_BYTES }, - >, -) -> eyre::Result> -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, - [(); S::FINALIZED_HEADER_INDEX]:, -{ - let mut update = update.clone(); - let pubkeys_compressed = update - .next_sync_committee + update: &LightClientUpdate, +) -> eyre::Result> { + let update = update.clone(); + let next_sync_committee = update.next_sync_committee().clone(); + + let pubkeys_compressed = next_sync_committee .pubkeys .iter() - .map(|pk| pk.to_bytes().to_vec()) + .map(|pk| pk.serialize().to_vec()) .collect_vec(); - let mut sync_committee_branch = update.next_sync_committee_branch.as_ref().to_vec(); - - sync_committee_branch.insert( - 0, - update - .next_sync_committee - .aggregate_pubkey - .hash_tree_root() - .unwrap(), - ); + let mut sync_committee_branch = update.next_sync_committee_branch().as_ref().to_vec(); + + sync_committee_branch.insert(0, next_sync_committee.aggregate_pubkey.tree_hash_root()); + let (attested_header_beacon, finalized_header_beacon) = match update { + LightClientUpdate::Altair(_) => unimplemented!(), + LightClientUpdate::Capella(update) => ( + update.attested_header.beacon, + update.finalized_header.beacon, + ), + + LightClientUpdate::Deneb(update) => ( + update.attested_header.beacon, + update.finalized_header.beacon, + ), + }; assert!( - ssz_rs::is_valid_merkle_branch( - update.next_sync_committee.pubkeys.hash_tree_root().unwrap(), - &sync_committee_branch - .iter() - .map(|n| n.as_ref()) - .collect_vec(), + merkle_proof::verify_merkle_proof( + next_sync_committee.pubkeys.tree_hash_root(), + &sync_committee_branch, S::SYNC_COMMITTEE_PUBKEYS_DEPTH, S::SYNC_COMMITTEE_PUBKEYS_ROOT_INDEX, - update.attested_header.beacon.state_root, - ) - .is_ok(), + attested_header_beacon.state_root, + ), "Execution payload merkle proof verification failed" ); let args = CommitteeUpdateArgs:: { pubkeys_compressed, - finalized_header: update.finalized_header.beacon.clone(), + finalized_header: finalized_header_beacon, sync_committee_branch: sync_committee_branch .into_iter() - .map(|n| n.to_vec()) + .map(|n| n.0.to_vec()) .collect_vec(), _spec: PhantomData, }; @@ -107,8 +90,10 @@ where #[cfg(test)] mod tests { + use std::time::Duration; + use super::*; - use beacon_api_client::mainnet::Client as MainnetClient; + use eth2::{SensitiveUrl, Timeouts}; use eth_types::Testnet; use halo2_base::halo2_proofs::halo2curves::bn256::Bn256; use halo2_base::halo2_proofs::poly::kzg::commitment::ParamsKZG; @@ -119,16 +104,18 @@ mod tests { halo2_base::gates::circuit::CircuitBuilderStage, util::{AppCircuit, Eth2ConfigPinning, Halo2ConfigPinning}, }; - use reqwest::Url; use snark_verifier_sdk::CircuitExt; #[tokio::test] async fn test_rotation_circuit_sepolia() { const CONFIG_PATH: &str = "../lightclient-circuits/config/committee_update_testnet.json"; - const K: u32 = 21; - let client = - MainnetClient::new(Url::parse("https://lodestar-sepolia.chainsafe.io").unwrap()); - let witness = fetch_rotation_args::(&client).await.unwrap(); + const K: u32 = 20; + const URL: &str = "https://lodestar-sepolia.chainsafe.io"; + let client = BeaconNodeHttpClient::new( + SensitiveUrl::parse(URL).unwrap(), + Timeouts::set_all(Duration::from_secs(10)), + ); + let witness = fetch_rotation_args::(&client).await.unwrap(); let pinning = Eth2ConfigPinning::from_path(CONFIG_PATH); let params: ParamsKZG = gen_srs(K); @@ -157,9 +144,12 @@ mod tests { &CommitteeUpdateArgs::::default(), None, ); - let client = - MainnetClient::new(Url::parse("https://lodestar-sepolia.chainsafe.io").unwrap()); - let witness = fetch_rotation_args::(&client).await.unwrap(); + const URL: &str = "https://lodestar-sepolia.chainsafe.io"; + let client = BeaconNodeHttpClient::new( + SensitiveUrl::parse(URL).unwrap(), + Timeouts::set_all(Duration::from_secs(10)), + ); + let witness = fetch_rotation_args::(&client).await.unwrap(); CommitteeUpdateCircuit::::gen_snark_shplonk( ¶ms, diff --git a/preprocessor/src/step.rs b/preprocessor/src/step.rs index 62a8d9b..dec6a8d 100644 --- a/preprocessor/src/step.rs +++ b/preprocessor/src/step.rs @@ -4,154 +4,178 @@ use std::marker::PhantomData; -use beacon_api_client::Client; -use beacon_api_client::{BlockId, ClientTypes, StateId}; use eth_types::Spec; -use ethereum_consensus_types::bls::BlsPublicKey; -use ethereum_consensus_types::signing::{compute_domain, DomainType}; -use ethereum_consensus_types::{ForkData, LightClientBootstrap, LightClientFinalityUpdate}; use itertools::Itertools; use lightclient_circuits::witness::SyncStepArgs; -use ssz_rs::Vector; -use ssz_rs::{Merkleized, Node}; -use crate::{get_light_client_bootstrap, get_light_client_finality_update}; +use blst::min_pk as bls; +use eth2::types::StateId; +use eth2::BeaconNodeHttpClient; +use ethereum_types::Domain; +use ethereum_types::{EthSpec, FixedVector, LightClientFinalityUpdate, PublicKeyBytes}; +use tree_hash::{Hash256, TreeHash}; /// Fetches the latest `LightClientFinalityUpdate`` and the current sync committee (from LightClientBootstrap) and converts it to a [`SyncStepArgs`] witness. -pub async fn fetch_step_args( - client: &Client, -) -> eyre::Result> -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, -{ - let finality_update = get_light_client_finality_update(client).await?; - let block_root = client - .get_beacon_block_root(BlockId::Slot(finality_update.finalized_header.beacon.slot)) +pub async fn fetch_step_args( + client: &BeaconNodeHttpClient, +) -> eyre::Result> { + let finality_update = client + .get_beacon_light_client_finality_update::() .await - .unwrap(); - let bootstrap: LightClientBootstrap< - { S::SYNC_COMMITTEE_SIZE }, - { S::SYNC_COMMITTEE_DEPTH }, - { S::BYTES_PER_LOGS_BLOOM }, - { S::MAX_EXTRA_DATA_BYTES }, - > = get_light_client_bootstrap(client, block_root).await?; + .map_err(|e| eyre::eyre!("Failed to get finality update: {:?}", e))? + .ok_or(eyre::eyre!("Failed to get finality update: None"))? + .data; + + let block_root = match &finality_update { + LightClientFinalityUpdate::Altair(_) => unimplemented!(), + LightClientFinalityUpdate::Capella(header) => { + header.finalized_header.beacon.canonical_root() + } + LightClientFinalityUpdate::Deneb(header) => header.finalized_header.beacon.canonical_root(), + }; - let pubkeys_compressed = bootstrap.current_sync_committee.pubkeys; + let bootstrap = client + .get_light_client_bootstrap::(block_root) + .await + .map_err(|e| eyre::eyre!("Failed to get bootstrap: {:?}", e))? + .ok_or(eyre::eyre!("Failed to get bootstrap: None"))? + .data; - let attested_state_id = finality_update.attested_header.beacon.state_root; + let pubkeys_compressed = &bootstrap.current_sync_committee().pubkeys; + + let attested_state_id = match &finality_update { + LightClientFinalityUpdate::Altair(_) => unimplemented!(), + LightClientFinalityUpdate::Capella(header) => header.attested_header.beacon.state_root, + LightClientFinalityUpdate::Deneb(header) => header.attested_header.beacon.state_root, + }; let fork_version = client - .get_fork(StateId::Root(attested_state_id)) - .await? + .get_beacon_states_fork(StateId::Root(attested_state_id)) + .await + .map_err(|e| eyre::eyre!("Failed to get fork version: {:?}", e))? + .ok_or(eyre::eyre!("Failed to get fork version: None"))? + .data .current_version; - let genesis_validators_root = client.get_genesis_details().await?.genesis_validators_root; - let fork_data = ForkData { - genesis_validators_root, + + let genesis_validators_root = client + .get_beacon_genesis() + .await + .map_err(|e| eyre::eyre!("Failed to get genesis validators root: {:?}", e))? + .data + .genesis_validators_root; + + let domain = S::EthSpec::default_spec().compute_domain( + Domain::SyncCommittee, fork_version, - }; - let domain = compute_domain(DomainType::SyncCommittee, &fork_data)?; + genesis_validators_root, + ); - step_args_from_finality_update(finality_update, pubkeys_compressed, domain).await + step_args_from_finality_update(finality_update, pubkeys_compressed, domain.into()).await } /// Converts a [`LightClientFinalityUpdate`] to a [`SyncStepArgs`] witness. pub async fn step_args_from_finality_update( - finality_update: LightClientFinalityUpdate< - { S::SYNC_COMMITTEE_SIZE }, - { S::FINALIZED_HEADER_DEPTH }, - { S::BYTES_PER_LOGS_BLOOM }, - { S::MAX_EXTRA_DATA_BYTES }, - >, - pubkeys_compressed: Vector, + finality_update: LightClientFinalityUpdate, + pubkeys_compressed: &FixedVector::SyncCommitteeSize>, domain: [u8; 32], ) -> eyre::Result> { let pubkeys_uncompressed = pubkeys_compressed .iter() - .map(|pk| pk.decompressed_bytes()) - .collect_vec(); - - let execution_payload_root = finality_update - .finalized_header - .execution - .clone() - .hash_tree_root()? - .to_vec(); - let execution_payload_branch = finality_update - .finalized_header - .execution_branch - .iter() - .map(|n| n.0.to_vec()) - .collect_vec(); + .map(|pk| { + bls::PublicKey::uncompress(&pk.serialize()) + .map_err(|e| eyre::eyre!("Failed to uncompress public key: {:?}", e)) + .map(|k| bls::PublicKey::serialize(&k)) + .map(|b| b.to_vec()) + }) + .collect::>, _>>()?; + + let (execution_payload_root, execution_payload_branch) = match finality_update { + LightClientFinalityUpdate::Altair(_) => unimplemented!(), + LightClientFinalityUpdate::Capella(ref header) => { + let finalized_header = &header.finalized_header; + + ( + finalized_header.execution.tree_hash_root().0.to_vec(), + finalized_header + .execution_branch + .iter() + .map(|n| n.0.to_vec()) + .collect_vec(), + ) + } + LightClientFinalityUpdate::Deneb(ref header) => { + let finalized_header = &header.finalized_header; + + ( + finalized_header.execution.tree_hash_root().0.to_vec(), + finalized_header + .execution_branch + .iter() + .map(|n| n.0.to_vec()) + .collect_vec(), + ) + } + }; + + let attested_header_beacon = match &finality_update { + LightClientFinalityUpdate::Altair(_) => unimplemented!(), + LightClientFinalityUpdate::Capella(update) => update.attested_header.beacon.clone(), + + LightClientFinalityUpdate::Deneb(update) => update.attested_header.beacon.clone(), + }; + + let finalized_header_beacon = match &finality_update { + LightClientFinalityUpdate::Altair(_) => unimplemented!(), + LightClientFinalityUpdate::Capella(update) => update.finalized_header.beacon.clone(), + + LightClientFinalityUpdate::Deneb(update) => update.finalized_header.beacon.clone(), + }; assert!( - ssz_rs::is_valid_merkle_branch( - Node::try_from(execution_payload_root.as_slice())?, - &execution_payload_branch, + merkle_proof::verify_merkle_proof( + Hash256::from_slice(&execution_payload_root), + &execution_payload_branch + .iter() + .map(|n| Hash256::from_slice(n)) + .collect_vec(), S::EXECUTION_STATE_ROOT_DEPTH, S::EXECUTION_STATE_ROOT_INDEX, - finality_update.finalized_header.beacon.body_root, - ) - .is_ok(), + finalized_header_beacon.body_root, + ), "Execution payload merkle proof verification failed" ); assert!( - ssz_rs::is_valid_merkle_branch( - finality_update - .finalized_header - .beacon - .clone() - .hash_tree_root() - .unwrap(), - &finality_update - .finality_branch - .iter() - .map(|n| n.as_ref()) - .collect_vec(), + merkle_proof::verify_merkle_proof( + finalized_header_beacon.tree_hash_root(), + finality_update.finality_branch(), S::FINALIZED_HEADER_DEPTH, S::FINALIZED_HEADER_INDEX, - finality_update.attested_header.beacon.state_root, - ) - .is_ok(), + attested_header_beacon.state_root, + ), "Finality merkle proof verification failed" ); Ok(SyncStepArgs { signature_compressed: finality_update - .sync_aggregate + .sync_aggregate() .sync_committee_signature - .to_bytes() + .serialize() .to_vec(), pubkeys_uncompressed, pariticipation_bits: finality_update - .sync_aggregate + .sync_aggregate() .sync_committee_bits .iter() - .by_vals() .collect_vec(), - attested_header: finality_update.attested_header.beacon, - finalized_header: finality_update.finalized_header.beacon, + attested_header: attested_header_beacon, + finalized_header: finalized_header_beacon, finality_branch: finality_update - .finality_branch - .iter() - .map(|n| n.0.to_vec()) - .collect_vec(), - execution_payload_root: finality_update - .finalized_header - .execution - .clone() - .hash_tree_root() - .unwrap() - .to_vec(), - execution_payload_branch: finality_update - .finalized_header - .execution_branch + .finality_branch() .iter() .map(|n| n.0.to_vec()) .collect_vec(), + execution_payload_root, + execution_payload_branch, domain, _spec: PhantomData, }) @@ -159,6 +183,9 @@ pub async fn step_args_from_finality_update( #[cfg(test)] mod tests { + use std::time::Duration; + + use eth2::{SensitiveUrl, Timeouts}; use eth_types::Testnet; use halo2_base::halo2_proofs::halo2curves::bn256::Bn256; use halo2_base::halo2_proofs::poly::kzg::commitment::ParamsKZG; @@ -171,16 +198,16 @@ mod tests { use snark_verifier_sdk::CircuitExt; use super::*; - use beacon_api_client::mainnet::Client as MainnetClient; - use reqwest::Url; #[tokio::test] async fn test_sync_circuit_sepolia() { const K: u32 = 21; - let client = - MainnetClient::new(Url::parse("https://lodestar-sepolia.chainsafe.io").unwrap()); - - let witness = fetch_step_args::(&client).await.unwrap(); + const URL: &str = "https://lodestar-sepolia.chainsafe.io"; + let client = BeaconNodeHttpClient::new( + SensitiveUrl::parse(URL).unwrap(), + Timeouts::set_all(Duration::from_secs(10)), + ); + let witness = fetch_step_args::(&client).await.unwrap(); let params: ParamsKZG = gen_srs(K); let circuit = StepCircuit::::create_circuit( @@ -208,9 +235,12 @@ mod tests { &SyncStepArgs::::default(), None, ); - let client = - MainnetClient::new(Url::parse("https://lodestar-sepolia.chainsafe.io").unwrap()); - let witness = fetch_step_args::(&client).await.unwrap(); + const URL: &str = "https://lodestar-sepolia.chainsafe.io"; + let client = BeaconNodeHttpClient::new( + SensitiveUrl::parse(URL).unwrap(), + Timeouts::set_all(Duration::from_secs(10)), + ); + let witness = fetch_step_args::(&client).await.unwrap(); StepCircuit::::gen_snark_shplonk( ¶ms, diff --git a/prover/Cargo.toml b/prover/Cargo.toml index da8a49b..9908f9c 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -39,6 +39,7 @@ serde_json.workspace = true getset.workspace = true log.workspace = true url = "2" +blst = { version = "0.3.11", default-features = false, features = [ "portable", "no-threads" ]} # ethereum ethers = { version = "2.0.7", default_features = false, features = [ @@ -47,9 +48,12 @@ ethers = { version = "2.0.7", default_features = false, features = [ ] } primitive-types = "0.12.2" reqwest = "0.11.22" -beacon-api-client.workspace = true -ethereum-consensus-types.workspace = true -ssz_rs.workspace = true + + +ethereum-types = {workspace = true, default-features = false } +eth2.workspace = true +tree_hash.workspace = true +ethereum_ssz = { workspace = true } [features] default = [] diff --git a/prover/src/cli.rs b/prover/src/cli.rs index d6f2b55..0e8aeb4 100644 --- a/prover/src/cli.rs +++ b/prover/src/cli.rs @@ -25,16 +25,7 @@ use halo2_solidity_verifier_new::{ pub(crate) async fn spec_app( proof: ProofCmd, base_args: &BaseArgs, -) -> eyre::Result<()> -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, - [(); S::FINALIZED_HEADER_INDEX]:, -{ +) -> eyre::Result<()> { match proof { ProofCmd::SyncStep { operation, diff --git a/prover/src/rpc.rs b/prover/src/rpc.rs index edc78bf..34c8980 100644 --- a/prover/src/rpc.rs +++ b/prover/src/rpc.rs @@ -4,6 +4,9 @@ use ark_std::{end_timer, start_timer}; use axum::{http::StatusCode, response::IntoResponse, routing::post, Router}; +use ethereum_types::{ + EthSpec, FixedVector, ForkName, LightClientFinalityUpdate, LightClientUpdate, PublicKeyBytes, +}; use ethers::prelude::*; use jsonrpc_v2::{Data, RequestObject as JsonRpcRequestObject}; use jsonrpc_v2::{Error as JsonRpcError, Params}; @@ -17,7 +20,9 @@ use preprocessor::{rotation_args_from_update, step_args_from_finality_update}; use snark_verifier_sdk::evm::encode_calldata; use snark_verifier_sdk::{halo2::aggregation::AggregationCircuit, Snark}; use spectre_prover::prover::ProverState; +use ssz::Decode; use std::path::{Path, PathBuf}; +use std::str::FromStr; use std::sync::Arc; pub type JsonRpcServerState = Arc>; @@ -29,16 +34,7 @@ use crate::rpc_api::{ pub(crate) fn jsonrpc_server( state: ProverState, -) -> JsonRpcServer -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, - [(); S::FINALIZED_HEADER_INDEX]:, -{ +) -> JsonRpcServer { JsonRpcServer::new() .with_data(Data::new(state)) .with_method( @@ -55,16 +51,7 @@ where pub(crate) async fn gen_evm_proof_committee_update_handler( Data(state): Data, Params(params): Params, -) -> Result -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, - [(); S::FINALIZED_HEADER_INDEX]:, -{ +) -> Result { let _permit = state .concurrency .clone() @@ -76,9 +63,19 @@ where let GenProofCommitteeUpdateParams { light_client_update, + fork_name, } = params; - let update = ssz_rs::deserialize(&light_client_update)?; + let fork_name = ForkName::from_str(&fork_name) + .map_err(|e| JsonRpcError::internal(format!("Failed to parse fork version: {}", e)))?; + + let update = LightClientUpdate::::from_ssz_bytes(&light_client_update, fork_name) + .map_err(|e| { + JsonRpcError::internal(format!( + "Failed to deserialize light client update: {:?}", + e + )) + })?; let witness = rotation_args_from_update(&update).await?; let params = state.params.get(state.committee_update.degree()).unwrap(); @@ -114,13 +111,7 @@ where pub(crate) async fn gen_evm_proof_sync_step_compressed_handler( Data(state): Data, Params(params): Params, -) -> Result -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, -{ +) -> Result { let _permit = state .concurrency .clone() @@ -134,11 +125,28 @@ where light_client_finality_update, domain, pubkeys, + fork_name, } = params; - let update = ssz_rs::deserialize(&light_client_finality_update)?; - let pubkeys = ssz_rs::deserialize(&pubkeys)?; - let witness = step_args_from_finality_update(update, pubkeys, domain).await?; + let fork_name = ForkName::from_str(&fork_name) + .map_err(|e| JsonRpcError::internal(format!("Failed to parse fork version: {}", e)))?; + + let update = LightClientFinalityUpdate::::from_ssz_bytes( + &light_client_finality_update, + fork_name, + ) + .map_err(|e| { + JsonRpcError::internal(format!( + "Failed to deserialize light client finality update: {:?}", + e + )) + })?; + let pubkeys = + FixedVector::::SyncCommitteeSize>::from_ssz_bytes( + &pubkeys, + ) + .map_err(|e| JsonRpcError::internal(format!("Failed to deserialize pubkeys: {:?}", e)))?; + let witness = step_args_from_finality_update(update, &pubkeys, domain).await?; let params = state.params.get(state.step.degree()).unwrap(); let snark = gen_uncompressed_snark::>( @@ -185,16 +193,7 @@ pub async fn run_rpc( config_dir: impl AsRef, build_dir: impl AsRef, concurrency: usize, -) -> Result<(), eyre::Error> -where - [(); S::SYNC_COMMITTEE_SIZE]:, - [(); S::FINALIZED_HEADER_DEPTH]:, - [(); S::BYTES_PER_LOGS_BLOOM]:, - [(); S::MAX_EXTRA_DATA_BYTES]:, - [(); S::SYNC_COMMITTEE_ROOT_INDEX]:, - [(); S::SYNC_COMMITTEE_DEPTH]:, - [(); S::FINALIZED_HEADER_INDEX]:, -{ +) -> Result<(), eyre::Error> { let tcp_listener = tokio::net::TcpListener::bind(format!("0.0.0.0:{}", port)).await?; let timer = start_timer!(|| "Load Prover State and Context"); let state = ProverState::new::(config_dir.as_ref(), build_dir.as_ref(), concurrency); diff --git a/prover/src/rpc_api.rs b/prover/src/rpc_api.rs index a65ba17..34ef230 100644 --- a/prover/src/rpc_api.rs +++ b/prover/src/rpc_api.rs @@ -14,7 +14,7 @@ pub struct GenProofStepParams { // Serializing as Vec so that we can differentiate between Mainnet, Testnet, Minimal at runtime pub light_client_finality_update: Vec, pub pubkeys: Vec, - + pub fork_name: String, pub domain: [u8; 32], } @@ -22,6 +22,7 @@ pub struct GenProofStepParams { pub struct GenProofCommitteeUpdateParams { // Serializing as Vec so that we can differentiate between Mainnet, Testnet, Minimal at runtime pub light_client_update: Vec, + pub fork_name: String, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/prover/src/utils.rs b/prover/src/utils.rs index e24670b..d84e9f1 100644 --- a/prover/src/utils.rs +++ b/prover/src/utils.rs @@ -2,58 +2,63 @@ // Code: https://github.com/ChainSafe/Spectre // SPDX-License-Identifier: LGPL-3.0-only -use std::{ops::Deref, sync::Arc}; +use std::{sync::Arc, time::Duration}; -use beacon_api_client::{BlockId, VersionedValue}; +use crate::args::UtilsCmd; +use blst::min_pk as bls; +use eth2::{types::BlockId, BeaconNodeHttpClient, SensitiveUrl, Timeouts}; use eth_types::LIMB_BITS; -use ethereum_consensus_types::LightClientBootstrap; +use ethereum_types::{LightClientBootstrap, MainnetEthSpec}; use itertools::Itertools; use lightclient_circuits::poseidon::poseidon_committee_commitment_from_uncompressed; -use ssz_rs::Merkleized; -use url::Url; - -use beacon_api_client::mainnet::Client as MainnetBeaconClient; - -use crate::args::UtilsCmd; +use tree_hash::TreeHash; pub(crate) async fn utils_cli(method: UtilsCmd) -> eyre::Result<()> { match method { UtilsCmd::CommitteePoseidon { beacon_api } => { - let reqwest_client = reqwest::Client::new(); - let beacon_client = Arc::new(MainnetBeaconClient::new_with_client( - reqwest_client.clone(), - Url::parse(&beacon_api).unwrap(), + let beacon_client = Arc::new(BeaconNodeHttpClient::new( + SensitiveUrl::parse(&beacon_api).unwrap(), + Timeouts::set_all(Duration::from_secs(10)), )); - let block = beacon_client - .get_beacon_block_root(BlockId::Head) + + let block_root = beacon_client + .get_beacon_blocks_root(BlockId::Head) .await - .unwrap(); - let route = format!("eth/v1/beacon/light_client/bootstrap/{block:?}"); - let mut bootstrap = match beacon_client - .get::>>(&route) + .unwrap() + .unwrap() + .data + .root; + + let bootstrap = beacon_client + .get_light_client_bootstrap::(block_root) .await - { - Ok(v) => v.data, - Err(e) => { - return Err(eyre::eyre!("Failed to fetch bootstrap: {}", e)); - } - }; + .map_err(|e| eyre::eyre!("Failed to get bootstrap: {:?}", e))? + .ok_or(eyre::eyre!("Failed to get bootstrap: None"))? + .data; + + let sync_period = match bootstrap { + LightClientBootstrap::Altair(_) => unimplemented!("Altair not implemented"), + LightClientBootstrap::Capella(ref bootstrap) => bootstrap.header.beacon.slot, + LightClientBootstrap::Deneb(ref bootstrap) => bootstrap.header.beacon.slot, + } / (32 * 256); - let sync_period = bootstrap.header.beacon.slot / (32 * 256); println!("Sync period: {}", sync_period); + let pubkeys_uncompressed = bootstrap - .current_sync_committee + .current_sync_committee() .pubkeys .iter() - .map(|pk| pk.decompressed_bytes()) + .map(|pk| { + bls::PublicKey::uncompress(&pk.serialize()) + .unwrap() + .serialize() + .to_vec() + }) .collect_vec(); - let ssz_root = bootstrap - .current_sync_committee - .pubkeys - .hash_tree_root() - .unwrap(); - println!("SSZ root: {:?}", hex::encode(ssz_root.deref())); + let ssz_root = bootstrap.current_sync_committee().pubkeys.tree_hash_root(); + + println!("SSZ root: {:?}", hex::encode(ssz_root.0)); let mut committee_poseidon = poseidon_committee_commitment_from_uncompressed(&pubkeys_uncompressed, LIMB_BITS) diff --git a/rust-toolchain b/rust-toolchain index 9df1964..96f6912 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -nightly-2024-01-08 +nightly-2024-04-01 \ No newline at end of file diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml index 0536fb8..02964d0 100644 --- a/test-utils/Cargo.toml +++ b/test-utils/Cargo.toml @@ -5,19 +5,22 @@ edition = "2021" [dependencies] lightclient-circuits = { workspace = true } -ethereum-consensus-types = { workspace = true } eth-types = { workspace = true } -ssz_rs = { workspace = true } halo2curves = { workspace = true } halo2-base = { workspace = true } + +ethereum-types = {workspace = true, default-features = false } +tree_hash.workspace = true +ethereum_ssz = { workspace = true } + + itertools = "0.11.0" hex = "0.4.3" serde = { version = "1.0.130", features = ["derive"] } anyhow = "1.0.75" ethers = "2.0.10" -test-utils = { git = "https://github.com/ChainSafe/ethereum-consensus-types", branch = "capella" } - snap = "1.1.0" serde_yaml = "0.9.19" +blst = { version = "0.3.11", default-features = false, features = [ "portable", "no-threads" ]} \ No newline at end of file diff --git a/test-utils/src/execution_payload_header.rs b/test-utils/src/execution_payload_header.rs deleted file mode 100644 index aefdb50..0000000 --- a/test-utils/src/execution_payload_header.rs +++ /dev/null @@ -1,73 +0,0 @@ -// The Licensed Work is (c) 2023 ChainSafe -// Code: https://github.com/ChainSafe/Spectre -// SPDX-License-Identifier: LGPL-3.0-only - -use crate::{ - test_types::{ByteList, ByteVector, ExecutionAddress}, - U256_BYTE_COUNT, -}; -use ssz_rs::prelude::*; -use std::ops::Deref; -#[derive(Default, Debug, Clone, SimpleSerialize, PartialEq, Eq)] -#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub struct ExecutionPayloadHeader< - const BYTES_PER_LOGS_BLOOM: usize, - const MAX_EXTRA_DATA_BYTES: usize, -> { - pub parent_hash: Node, - pub fee_recipient: ExecutionAddress, - pub state_root: Node, - pub receipts_root: Node, - pub logs_bloom: ByteVector, - pub prev_randao: Node, - pub block_number: u64, - pub gas_limit: u64, - pub gas_used: u64, - pub timestamp: u64, - pub extra_data: ByteList, - pub base_fee_per_gas: U256, - pub block_hash: Node, - pub transactions_root: Node, - pub withdrawals_root: Node, -} - -impl - From< - ethereum_consensus_types::light_client::ExecutionPayloadHeader< - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - >, - > for ExecutionPayloadHeader -{ - fn from( - header: ethereum_consensus_types::light_client::ExecutionPayloadHeader< - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - >, - ) -> Self { - Self { - parent_hash: header.parent_hash, - fee_recipient: ByteVector( - Vector::try_from(header.fee_recipient.0.as_ref().to_vec()).unwrap(), - ), - state_root: header.state_root.deref().try_into().unwrap(), - receipts_root: header.receipts_root.deref().try_into().unwrap(), - logs_bloom: ByteVector( - Vector::try_from(header.logs_bloom.0.as_ref().to_vec()).unwrap(), - ), - prev_randao: header.prev_randao, - block_number: header.block_number, - gas_limit: header.gas_limit, - gas_used: header.gas_used, - timestamp: header.timestamp, - extra_data: ByteList(List::try_from(header.extra_data.0.as_ref().to_vec()).unwrap()), - base_fee_per_gas: U256::from_le_bytes::( - header.base_fee_per_gas.as_le_slice().try_into().unwrap(), - ), - block_hash: header.block_hash, - transactions_root: header.transactions_root, - - withdrawals_root: header.withdrawals_root, - } - } -} diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs index cd3f810..4f5e28f 100644 --- a/test-utils/src/lib.rs +++ b/test-utils/src/lib.rs @@ -5,40 +5,42 @@ #![allow(incomplete_features)] #![feature(generic_const_exprs)] -mod execution_payload_header; mod test_types; - -use crate::execution_payload_header::ExecutionPayloadHeader; -use crate::test_types::{ByteVector, TestMeta, TestStep}; +use crate::test_types::{TestMeta, TestStep}; +use blst::min_pk as bls; use eth_types::{Minimal, LIMB_BITS}; -use ethereum_consensus_types::presets::minimal::{ - LightClientBootstrap, LightClientUpdateCapella, BYTES_PER_LOGS_BLOOM, MAX_EXTRA_DATA_BYTES, +use ethereum_types::{ + BeaconBlockHeader, Domain, EthSpec, ExecutionPayloadHeader, Hash256, + LightClientBootstrapCapella, LightClientUpdateCapella, MinimalEthSpec, SyncCommittee, }; -use ethereum_consensus_types::signing::{compute_domain, DomainType}; -use ethereum_consensus_types::{BeaconBlockHeader, SyncCommittee}; -use ethereum_consensus_types::{ForkData, Root}; +use ethers::types::H256; use itertools::Itertools; use lightclient_circuits::poseidon::poseidon_committee_commitment_from_uncompressed; use lightclient_circuits::witness::{CommitteeUpdateArgs, SyncStepArgs}; -use ssz_rs::prelude::*; -use ssz_rs::Merkleized; -use std::ops::Deref; -use std::path::Path; -use test_utils::{load_snappy_ssz, load_yaml}; +use serde::Deserialize; +use tree_hash::TreeHash; -pub(crate) const U256_BYTE_COUNT: usize = 32; +use std::fs::File; +use std::io::Read; +use std::path::Path; +use std::sync::Arc; // loads the boostrap on the path and return the initial sync committee poseidon and sync period pub fn get_initial_sync_committee_poseidon( path: &Path, ) -> anyhow::Result<(usize, ethers::prelude::U256)> { - let bootstrap: LightClientBootstrap = + let bootstrap: LightClientBootstrapCapella = load_snappy_ssz(path.join("bootstrap.ssz_snappy").to_str().unwrap()).unwrap(); let pubkeys_uncompressed = bootstrap .current_sync_committee .pubkeys .iter() - .map(|pk| pk.decompressed_bytes()) + .map(|pk| { + bls::PublicKey::uncompress(&pk.serialize()) + .unwrap() + .serialize() + .to_vec() + }) .collect_vec(); let committee_poseidon = poseidon_committee_commitment_from_uncompressed(&pubkeys_uncompressed, LIMB_BITS); @@ -49,28 +51,26 @@ pub fn get_initial_sync_committee_poseidon Root { +pub fn validators_root_from_test_path(path: &Path) -> H256 { let meta: TestMeta = load_yaml(path.join("meta.yaml").to_str().unwrap()); - Root::try_from( + H256( hex::decode(meta.genesis_validators_root.trim_start_matches("0x")) .unwrap() - .as_slice(), + .try_into() + .unwrap(), ) - .unwrap() } // Load the updates for a given test and only includes the first sequence of steps that Spectre can perform // e.g. the the steps are cut at the first `ForceUpdate` step -pub fn valid_updates_from_test_path( - path: &Path, -) -> Vec> { +pub fn valid_updates_from_test_path(path: &Path) -> Vec> { let steps: Vec = load_yaml(path.join("steps.yaml").to_str().unwrap()); let updates = steps .iter() .take_while(|step| matches!(step, TestStep::ProcessUpdate { .. })) .filter_map(|step| match step { TestStep::ProcessUpdate { update, .. } => { - let update: LightClientUpdateCapella = load_snappy_ssz( + let update: LightClientUpdateCapella = load_snappy_ssz( path.join(format!("{}.ssz_snappy", update)) .to_str() .unwrap(), @@ -87,7 +87,7 @@ pub fn valid_updates_from_test_path( pub fn read_test_files_and_gen_witness( path: &Path, ) -> (SyncStepArgs, CommitteeUpdateArgs) { - let bootstrap: LightClientBootstrap = + let bootstrap: LightClientBootstrapCapella = load_snappy_ssz(path.join("bootstrap.ssz_snappy").to_str().unwrap()).unwrap(); let genesis_validators_root = validators_root_from_test_path(path); @@ -102,18 +102,17 @@ pub fn read_test_files_and_gen_witness( let mut sync_committee_branch = updates[0] .next_sync_committee_branch .iter() - .map(|n| n.deref().to_vec()) + .map(|n| n.0.to_vec()) .collect_vec(); - let agg_pubkeys_compressed = updates[0] + let agg_pk = updates[0] .next_sync_committee .aggregate_pubkey - .to_bytes() + .tree_hash_root() + .0 .to_vec(); - let mut agg_pk: ByteVector<48> = ByteVector(Vector::try_from(agg_pubkeys_compressed).unwrap()); - - sync_committee_branch.insert(0, agg_pk.hash_tree_root().unwrap().deref().to_vec()); + sync_committee_branch.insert(0, agg_pk); let rotation_wit = CommitteeUpdateArgs:: { pubkeys_compressed: updates[0] @@ -121,7 +120,7 @@ pub fn read_test_files_and_gen_witness( .pubkeys .iter() .cloned() - .map(|pk| pk.to_bytes().to_vec()) + .map(|pk| pk.serialize().to_vec()) .collect_vec(), finalized_header: sync_wit.attested_header.clone(), sync_committee_branch, @@ -130,16 +129,16 @@ pub fn read_test_files_and_gen_witness( (sync_wit, rotation_wit) } -fn to_sync_ciruit_witness( - committee: SyncCommittee, - light_client_update: &LightClientUpdateCapella, - genesis_validators_root: Root, +fn to_sync_ciruit_witness( + committee: Arc>, + light_client_update: &LightClientUpdateCapella, + genesis_validators_root: Hash256, ) -> SyncStepArgs { let mut args = SyncStepArgs:: { signature_compressed: light_client_update .sync_aggregate .sync_committee_signature - .to_bytes() + .serialize() .to_vec(), ..Default::default() }; @@ -147,77 +146,39 @@ fn to_sync_ciruit_witness( let pubkeys_uncompressed = committee .pubkeys .iter() - .map(|pk| pk.decompressed_bytes()) + .map(|pk| { + bls::PublicKey::uncompress(&pk.serialize()) + .unwrap() + .serialize() + .to_vec() + }) .collect_vec(); args.pubkeys_uncompressed = pubkeys_uncompressed; args.pariticipation_bits = light_client_update .sync_aggregate .sync_committee_bits .iter() - .map(|b| *b) .collect(); args.attested_header = BeaconBlockHeader { slot: light_client_update.attested_header.beacon.slot, proposer_index: light_client_update.attested_header.beacon.proposer_index, - parent_root: Node::try_from( - light_client_update - .attested_header - .beacon - .parent_root - .as_ref(), - ) - .unwrap(), - state_root: Node::try_from( - light_client_update - .attested_header - .beacon - .state_root - .as_ref(), - ) - .unwrap(), - body_root: Node::try_from( - light_client_update - .attested_header - .beacon - .body_root - .as_ref(), - ) - .unwrap(), + parent_root: light_client_update.attested_header.beacon.parent_root, + state_root: light_client_update.attested_header.beacon.state_root, + body_root: light_client_update.attested_header.beacon.body_root, }; args.finalized_header = BeaconBlockHeader { slot: light_client_update.finalized_header.beacon.slot, proposer_index: light_client_update.finalized_header.beacon.proposer_index, - parent_root: Node::try_from( - light_client_update - .finalized_header - .beacon - .parent_root - .as_ref(), - ) - .unwrap(), - state_root: Node::try_from( - light_client_update - .finalized_header - .beacon - .state_root - .as_ref(), - ) - .unwrap(), - body_root: Node::try_from( - light_client_update - .finalized_header - .beacon - .body_root - .as_ref(), - ) - .unwrap(), + parent_root: light_client_update.finalized_header.beacon.parent_root, + state_root: light_client_update.finalized_header.beacon.state_root, + body_root: light_client_update.finalized_header.beacon.body_root, }; - let fork_data = ForkData { - fork_version: [3, 0, 0, 1], + let domain = MinimalEthSpec::default_spec().compute_domain( + Domain::SyncCommittee, + [3, 0, 0, 1], genesis_validators_root, - }; - let signing_domain = compute_domain(DomainType::SyncCommittee, &fork_data).unwrap(); - args.domain = signing_domain; + ); + args.domain = domain.into(); args.execution_payload_branch = light_client_update .finalized_header .execution_branch @@ -225,25 +186,59 @@ fn to_sync_ciruit_witness( .map(|b| b.0.as_ref().to_vec()) .collect(); args.execution_payload_root = { - let mut execution_payload_header: ExecutionPayloadHeader< - BYTES_PER_LOGS_BLOOM, - MAX_EXTRA_DATA_BYTES, - > = light_client_update + let execution_payload_header: ExecutionPayloadHeader = light_client_update .finalized_header .execution .clone() .into(); - execution_payload_header - .hash_tree_root() - .unwrap() - .deref() - .to_vec() + execution_payload_header.tree_hash_root().0.to_vec() }; args.finality_branch = light_client_update .finality_branch .iter() - .map(|b| b.deref().to_vec()) + .map(|b| b.0.to_vec()) .collect(); args } + +pub fn load_yaml Deserialize<'de>>(path: &str) -> T { + let mut file = File::open(path).unwrap_or_else(|_| { + panic!( + "File {} does not exist from dir {:?}", + path, + std::env::current_dir().unwrap() + ) + }); + let deserializer = serde_yaml::Deserializer::from_reader(&mut file); + let test_case: Result = + serde_yaml::with::singleton_map_recursive::deserialize(deserializer); + match test_case { + Ok(test_case) => test_case, + Err(err) => { + let content = std::fs::read_to_string(path).unwrap(); + panic!("{err} from {content} at {path:?}") + } + } +} + +pub fn load_snappy_ssz_bytes(path: &Path) -> Vec { + let mut file = File::open(path).unwrap(); + let mut data = vec![]; + file.read_to_end(&mut data).unwrap(); + + let mut decoder = snap::raw::Decoder::new(); + decoder.decompress_vec(&data).unwrap() +} + +pub fn load_snappy_ssz(path: &str) -> Option { + let path = Path::new(path); + if !path.exists() { + return None; + } + let buffer = load_snappy_ssz_bytes(path); + + let result = ::from_ssz_bytes(&buffer).unwrap(); + + Some(result) +} diff --git a/test-utils/src/test_types.rs b/test-utils/src/test_types.rs index 0fc3105..0f90606 100644 --- a/test-utils/src/test_types.rs +++ b/test-utils/src/test_types.rs @@ -2,8 +2,6 @@ // Code: https://github.com/ChainSafe/Spectre // SPDX-License-Identifier: LGPL-3.0-only -use ssz_rs::prelude::*; - #[allow(dead_code)] #[derive(Debug, serde::Deserialize)] pub struct TestMeta { @@ -43,10 +41,3 @@ pub struct RootAtSlot { pub beacon_root: String, pub execution_root: String, } - -// TODO: remove this once we have a better way to handle the `ssz_rs` dependency -#[derive(Debug, Default, Clone, PartialEq, SimpleSerialize, Eq)] -pub struct ByteVector(pub Vector); -#[derive(Default, Debug, Clone, PartialEq, Eq, SimpleSerialize)] -pub struct ByteList(pub List); -pub type ExecutionAddress = ByteVector<20>;