diff --git a/contract-tests/tests/step_input_encoding.rs b/contract-tests/tests/step_input_encoding.rs index bdf5316..ac4d0dc 100644 --- a/contract-tests/tests/step_input_encoding.rs +++ b/contract-tests/tests/step_input_encoding.rs @@ -46,7 +46,7 @@ impl From> for SyncStepInput { participation, finalized_header_root, execution_payload_root, - accumulator: Default::default() + accumulator: Default::default(), } } } @@ -68,7 +68,10 @@ async fn test_step_instance_commitment_evm_equivalence( let contract = SyncStepExternal::deploy(ethclient, ())?.send().await?; let result = contract - .to_public_inputs(SyncStepInput::from(witness), U256::from_little_endian(&instance[0][1].to_bytes())) + .to_public_inputs( + SyncStepInput::from(witness), + U256::from_little_endian(&instance[0][1].to_bytes()), + ) .call() .await?; let result_decoded = decode_solidity_u256_array(&result); diff --git a/lightclient-circuits/src/poseidon.rs b/lightclient-circuits/src/poseidon.rs index 2826e22..680b7bb 100644 --- a/lightclient-circuits/src/poseidon.rs +++ b/lightclient-circuits/src/poseidon.rs @@ -29,12 +29,12 @@ const R_P: usize = N_ROUNDS_PC[T - 2]; const R_F: usize = 8; /// Generates Poseidon hash commitment to a list of BLS12-381 Fq elements. -/// +/// /// Fields elements are initially represented as `NUM_LIMBS` limbs of `LIMB_BITS` bits each. /// By composing element two limbs in one, we reduce the number of inputs to Poseidon in half. -/// +/// /// Each Poseidon sponge absorbs `POSEIDON_SIZE`-2 elements and previos sponge output if it's not the first batch, ie. onion commitment. -/// +/// /// Assumes that LIMB_BITS * 2 < 254 (BN254). pub fn fq_array_poseidon<'a, F: Field>( ctx: &mut Context, @@ -74,7 +74,7 @@ pub fn fq_array_poseidon<'a, F: Field>( } /// Generates Poseidon hash commitment to a list of BLS12-381 Fq elements. -/// +/// /// This is the off-circuit analog of `fq_array_poseidon`. pub fn poseidon_hash_fq_array(elems: impl Iterator, limb_bits: usize) -> F { let limbs = elems diff --git a/lightclient-circuits/src/sync_step_circuit.rs b/lightclient-circuits/src/sync_step_circuit.rs index 541d132..1d829e0 100644 --- a/lightclient-circuits/src/sync_step_circuit.rs +++ b/lightclient-circuits/src/sync_step_circuit.rs @@ -439,7 +439,6 @@ impl AppCircuit for StepCircuit { } } - #[cfg(test)] mod tests { use std::fs; diff --git a/lightclient-circuits/src/util/circuit.rs b/lightclient-circuits/src/util/circuit.rs index b95de66..7805cc9 100644 --- a/lightclient-circuits/src/util/circuit.rs +++ b/lightclient-circuits/src/util/circuit.rs @@ -122,7 +122,8 @@ pub trait AppCircuit { ) -> ProvingKey { let pinning = Self::Pinning::from_path(pinning_path); let circuit = - Self::create_circuit(CircuitBuilderStage::Keygen, Some(pinning), witness, params).unwrap(); + Self::create_circuit(CircuitBuilderStage::Keygen, Some(pinning), witness, params) + .unwrap(); custom_read_pk(path, &circuit) } diff --git a/lightclient-circuits/src/witness/rotation.rs b/lightclient-circuits/src/witness/rotation.rs index fac74f9..0bdf1a5 100644 --- a/lightclient-circuits/src/witness/rotation.rs +++ b/lightclient-circuits/src/witness/rotation.rs @@ -10,7 +10,7 @@ use sha2::{Digest, Sha256}; use std::{iter, marker::PhantomData}; /// Input datum for the `CommitteeUpdateCircuit` to map next sync committee SSZ root in the finalized state root to the corresponding Poseidon commitment to the public keys. -/// +/// /// Assumes that public keys are BLS12-381 points on G1; `sync_committee_branch` is exactly `S::SYNC_COMMITTEE_PUBKEYS_DEPTH` hashes in lenght. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CommitteeUpdateArgs { diff --git a/lightclient-circuits/src/witness/step.rs b/lightclient-circuits/src/witness/step.rs index 3755ef3..9b84262 100644 --- a/lightclient-circuits/src/witness/step.rs +++ b/lightclient-circuits/src/witness/step.rs @@ -19,7 +19,7 @@ use super::mock_root; /// Input datum for the `StepCircuit` to verify `attested_header` singed by the lightclient sync committee, /// and the `execution_payload_root` via Merkle `finality_branch` against the `finalized_header` root. -/// +/// /// Assumes that aggregated BLS signarure is represented as a compressed G2 point and the public keys are uncompressed G1 points; /// `pariticipation_bits` vector has exactly `S::SYNC_COMMITTEE_SIZE`` bits; /// `finality_branch` and `execution_payload_branch` are exactly `S::FINALIZED_HEADER_DEPTH` and `S::EXECUTION_STATE_ROOT_DEPTH` long respectively. @@ -138,13 +138,9 @@ mod tests { const K: u32 = 20; let witness = SyncStepArgs::::default(); - let circuit = StepCircuit::::mock_circuit( - CircuitBuilderStage::Mock, - None, - &witness, - K, - ) - .unwrap(); + let circuit = + StepCircuit::::mock_circuit(CircuitBuilderStage::Mock, None, &witness, K) + .unwrap(); let prover = MockProver::::run(K, &circuit, circuit.instances()).unwrap(); prover.assert_satisfied_par(); diff --git a/preprocessor/src/lib.rs b/preprocessor/src/lib.rs index d243711..eaf68ea 100644 --- a/preprocessor/src/lib.rs +++ b/preprocessor/src/lib.rs @@ -12,13 +12,15 @@ use beacon_api_client::{BlockId, Client, ClientTypes, Value, VersionedValue}; use eth_types::Spec; use ethereum_consensus_types::bls::BlsSignature; use ethereum_consensus_types::{ - BeaconBlockHeader, LightClientBootstrap, LightClientFinalityUpdate, LightClientUpdateCapella, - Root, + BeaconBlockHeader, BlsPublicKey, ByteVector, LightClientBootstrap, LightClientFinalityUpdate, + LightClientUpdateCapella, Root, }; +use itertools::Itertools; +use lightclient_circuits::witness::{CommitteeUpdateArgs, SyncStepArgs}; pub use rotation::*; use serde::{Deserialize, Serialize}; -use ssz_rs::Node; +use ssz_rs::{Node, Vector}; pub use step::*; pub async fn get_light_client_update_at_period( @@ -122,3 +124,164 @@ pub async fn get_block_header( let block: BeaconHeaderSummary = client.get::>(&route).await?.data; Ok(block.header.message) } + +pub async fn light_client_update_to_args( + update: &mut 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, + 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, + }; + + let rotation_args = rotation::rotation_args_from_update(update).await?; + + let sync_args = + step::step_args_from_finality_update(finality_update, pubkeys_compressed, domain).await?; + + Ok((sync_args, rotation_args)) +} + +#[cfg(test)] +mod tests { + use beacon_api_client::StateId; + use eth_types::Testnet; + use ethereum_consensus_types::signing::{compute_domain, DomainType}; + use ethereum_consensus_types::ForkData; + use lightclient_circuits::committee_update_circuit::CommitteeUpdateCircuit; + use lightclient_circuits::halo2_proofs::{dev::MockProver, halo2curves::bn256::Fr}; + use lightclient_circuits::util::{Eth2ConfigPinning, Halo2ConfigPinning}; + use lightclient_circuits::{ + halo2_base::gates::circuit::CircuitBuilderStage, sync_step_circuit::StepCircuit, + util::AppCircuit, + }; + use snark_verifier_sdk::CircuitExt; + + use super::*; + use beacon_api_client::mainnet::Client as MainnetClient; + use reqwest::Url; + + #[tokio::test] + async fn test_both_circuit_sepolia() { + const K: u32 = 21; + let client = + MainnetClient::new(Url::parse("https://lodestar-sepolia.chainsafe.io").unwrap()); + + let block = get_block_header(&client, BlockId::Finalized).await.unwrap(); + let slot = block.slot; + let period = slot / (32 * 256); + + println!( + "Fetching light client update at current Slot: {} at Period: {}", + slot, period + ); + + // Fetch light client update and create circuit arguments + let (s, mut c) = { + let mut update = get_light_client_update_at_period(&client, period) + .await + .unwrap(); + + let block_root = client + .get_beacon_block_root(BlockId::Slot(slot)) + .await + .unwrap(); + + let bootstrap = get_light_client_bootstrap(&client, block_root) + .await + .unwrap(); + + let pubkeys_compressed = bootstrap.current_sync_committee.pubkeys; + + let fork_version = client + .get_fork(StateId::Head) + .await + .unwrap() + .current_version; + let genesis_validators_root = client + .get_genesis_details() + .await + .unwrap() + .genesis_validators_root; + let fork_data = ForkData { + genesis_validators_root, + fork_version, + }; + let domain = compute_domain(DomainType::SyncCommittee, &fork_data).unwrap(); + light_client_update_to_args::(&mut update, pubkeys_compressed, domain) + .await + .unwrap() + }; + + let mut finalized_sync_committee_branch = { + let block_root = client + .get_beacon_block_root(BlockId::Slot(s.finalized_header.slot)) + .await + .unwrap(); + + get_light_client_bootstrap::(&client, block_root) + .await + .unwrap() + .current_sync_committee_branch + .iter() + .map(|n| n.to_vec()) + .collect_vec() + }; + + // 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(); + c.sync_committee_branch = finalized_sync_committee_branch; + // Replaces the attested header with step circuits finalized header + c.finalized_header = s.finalized_header.clone(); + + let circuit = + StepCircuit::::mock_circuit(CircuitBuilderStage::Mock, None, &s, K) + .unwrap(); + + let prover = MockProver::::run(K, &circuit, circuit.instances()).unwrap(); + prover.assert_satisfied_par(); + + const CONFIG_PATH: &str = "../lightclient-circuits/config/committee_update_testnet.json"; + + let pinning = Eth2ConfigPinning::from_path(CONFIG_PATH); + let circuit = CommitteeUpdateCircuit::::mock_circuit( + CircuitBuilderStage::Mock, + Some(pinning), + &c, + K, + ) + .unwrap(); + + let prover = MockProver::::run(K, &circuit, circuit.instances()).unwrap(); + prover.assert_satisfied_par(); + } +} diff --git a/preprocessor/src/rotation.rs b/preprocessor/src/rotation.rs index 0f67ab1..d471a74 100644 --- a/preprocessor/src/rotation.rs +++ b/preprocessor/src/rotation.rs @@ -41,7 +41,7 @@ where /// Converts a [`LightClientUpdateCapella`] to a [`CommitteeUpdateArgs`] witness. pub async fn rotation_args_from_update( - update: &mut LightClientUpdateCapella< + update: &LightClientUpdateCapella< { S::SYNC_COMMITTEE_SIZE }, { S::SYNC_COMMITTEE_ROOT_INDEX }, { S::SYNC_COMMITTEE_DEPTH }, @@ -60,6 +60,7 @@ where [(); S::SYNC_COMMITTEE_DEPTH]:, [(); S::FINALIZED_HEADER_INDEX]:, { + let mut update = update.clone(); let pubkeys_compressed = update .next_sync_committee .pubkeys @@ -94,7 +95,7 @@ where let args = CommitteeUpdateArgs:: { pubkeys_compressed, - finalized_header: update.attested_header.beacon.clone(), + finalized_header: update.finalized_header.beacon.clone(), sync_committee_branch: sync_committee_branch .into_iter() .map(|n| n.to_vec()) @@ -121,7 +122,7 @@ mod tests { #[tokio::test] async fn test_rotation_circuit_sepolia() { - const CONFIG_PATH: &str = "../lightclient-circuits/config/committee_update.json"; + 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()); diff --git a/prover/src/rpc_client.rs b/prover/src/rpc_client.rs index 7af5f61..594a0cf 100644 --- a/prover/src/rpc_client.rs +++ b/prover/src/rpc_client.rs @@ -9,8 +9,9 @@ use serde::{de::DeserializeOwned, Deserialize, Serialize}; use url::Url; use crate::rpc_api::{ - CommitteeUpdateEvmProofResult, GenProofStepParams, SyncStepCompressedEvmProofResult, - RPC_EVM_PROOF_COMMITTEE_UPDATE_CIRCUIT_COMPRESSED, RPC_EVM_PROOF_STEP_CIRCUIT_COMPRESSED, GenProofCommitteeUpdateParams, + CommitteeUpdateEvmProofResult, GenProofCommitteeUpdateParams, GenProofStepParams, + SyncStepCompressedEvmProofResult, RPC_EVM_PROOF_COMMITTEE_UPDATE_CIRCUIT_COMPRESSED, + RPC_EVM_PROOF_STEP_CIRCUIT_COMPRESSED, }; /// Error object in a response diff --git a/prover/src/utils.rs b/prover/src/utils.rs index ba56ff5..9dabd3b 100644 --- a/prover/src/utils.rs +++ b/prover/src/utils.rs @@ -2,7 +2,7 @@ // Code: https://github.com/ChainSafe/Spectre // SPDX-License-Identifier: LGPL-3.0-only -use std::{env, ops::Deref, sync::Arc}; +use std::{ops::Deref, sync::Arc}; use beacon_api_client::{BlockId, VersionedValue}; use ethereum_consensus_types::LightClientBootstrap;