diff --git a/Cargo.lock b/Cargo.lock index 4be200c75..8459b3380 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5923,11 +5923,19 @@ dependencies = [ name = "ream-checkpoint-sync-lean" version = "0.1.0" dependencies = [ + "actix-web", "alloy-primitives", "anyhow", "ethereum_ssz", "ream-consensus-lean", + "ream-fork-choice-lean", + "ream-network-spec", + "ream-post-quantum-crypto", + "ream-rpc-lean", + "ream-storage", + "ream-sync", "reqwest", + "ssz_types", "tokio", "tracing", "tree_hash", diff --git a/crates/common/checkpoint_sync/lean/Cargo.toml b/crates/common/checkpoint_sync/lean/Cargo.toml index c4bf9900e..23bc05692 100644 --- a/crates/common/checkpoint_sync/lean/Cargo.toml +++ b/crates/common/checkpoint_sync/lean/Cargo.toml @@ -9,6 +9,15 @@ repository.workspace = true rust-version.workspace = true version.workspace = true +[features] +default = ["devnet1"] +devnet1 = [] +devnet2 = [ + "ream-consensus-lean/devnet2", + "ream-fork-choice-lean/devnet2", + "ream-rpc-lean/devnet2", +] + [dependencies] alloy-primitives.workspace = true anyhow.workspace = true @@ -21,5 +30,18 @@ tree_hash.workspace = true # ream dependencies ream-consensus-lean.workspace = true +[dev-dependencies] +actix-web.workspace = true +ssz_types.workspace = true +tokio.workspace = true + +# ream dependencies (tests) +ream-fork-choice-lean.workspace = true +ream-network-spec.workspace = true +ream-post-quantum-crypto.workspace = true +ream-rpc-lean.workspace = true +ream-storage.workspace = true +ream-sync.workspace = true + [lints] workspace = true diff --git a/crates/common/checkpoint_sync/lean/src/lib.rs b/crates/common/checkpoint_sync/lean/src/lib.rs index 0ec822c80..9846a163b 100644 --- a/crates/common/checkpoint_sync/lean/src/lib.rs +++ b/crates/common/checkpoint_sync/lean/src/lib.rs @@ -41,13 +41,120 @@ impl LeanCheckpointClient { } pub fn verify_checkpoint_state(state: &LeanState) -> bool { - if state.slot == 0 { - return false; - } - if state.validators.is_empty() { return false; } true } + +#[cfg(all(test, feature = "devnet2"))] +mod tests { + use std::net::TcpListener; + + use actix_web::{ + App, HttpServer, + web::{Data, scope}, + }; + use ream_consensus_lean::{ + attestation::{AggregatedAttestations, AttestationData}, + block::{BlockSignatures, BlockWithAttestation, SignedBlockWithAttestation}, + checkpoint::Checkpoint, + utils::generate_default_validators, + }; + use ream_fork_choice_lean::{genesis::setup_genesis, store::Store}; + use ream_network_spec::networks::{LeanNetworkSpec, lean_network_spec, set_lean_network_spec}; + use ream_post_quantum_crypto::leansig::signature::Signature; + use ream_rpc_lean::handlers::state::get_state; + use ream_storage::db::ReamDB; + use ream_sync::rwlock::Writer; + use reqwest::Url; + use ssz_types::VariableList; + use tree_hash::TreeHash; + + use super::{LeanCheckpointClient, verify_checkpoint_state}; + + async fn sample_store(no_of_validators: usize) -> Store { + set_lean_network_spec(LeanNetworkSpec::ephemery().into()); + let (genesis_block, genesis_state) = setup_genesis( + lean_network_spec().genesis_time, + generate_default_validators(no_of_validators), + ); + + let checkpoint = Checkpoint { + slot: genesis_block.slot, + root: genesis_block.tree_hash_root(), + }; + let signed_genesis_block = SignedBlockWithAttestation { + message: BlockWithAttestation { + proposer_attestation: AggregatedAttestations { + validator_id: genesis_block.proposer_index, + data: AttestationData { + slot: genesis_block.slot, + head: checkpoint, + target: checkpoint, + source: checkpoint, + }, + }, + block: genesis_block, + }, + signature: BlockSignatures { + attestation_signatures: VariableList::default(), + proposer_signature: Signature::blank(), + }, + }; + + let temp_path = std::env::temp_dir().join(format!( + "checkpoint_sync_lean_test_{}_{:?}", + std::process::id(), + std::thread::current().id() + )); + std::fs::create_dir_all(&temp_path).expect("Failed to create temp directory"); + let ream_db = ReamDB::new(temp_path).expect("Failed to init Ream Database"); + let lean_db = ream_db.init_lean_db().expect("Failed to init lean db"); + + Store::get_forkchoice_store(signed_genesis_block, genesis_state, lean_db, Some(0)) + .expect("Failed to create forkchoice store") + } + + #[tokio::test] + async fn test_client_fetches_and_deserializes_state() { + let store = sample_store(10).await; + let (_writer, reader) = Writer::new(store); + + let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); + let addr = listener.local_addr().expect("Failed to get local addr"); + + let server = HttpServer::new(move || { + App::new() + .app_data(Data::new(reader.clone())) + .service(scope("/lean/v0").service(get_state)) + }) + .listen(listener) + .expect("Failed to attach listener") + .run(); + + let server_handle = server.handle(); + tokio::spawn(server); + + let client = LeanCheckpointClient::new(); + let base_url = Url::parse(&format!("http://{addr}")).expect("Failed to parse base URL"); + + let state = client + .fetch_finalized_state(&base_url) + .await + .expect("Client failed to fetch finalized state"); + + assert_eq!(state.slot, 0); + assert!(verify_checkpoint_state(&state)); + + let (_, genesis_state) = setup_genesis( + lean_network_spec().genesis_time, + generate_default_validators(10), + ); + + assert_eq!(state, genesis_state); + + server_handle.stop(true).await; + } +} diff --git a/crates/common/consensus/beacon/src/electra/beacon_block.rs b/crates/common/consensus/beacon/src/electra/beacon_block.rs index c6a26bc8c..2e8506468 100644 --- a/crates/common/consensus/beacon/src/electra/beacon_block.rs +++ b/crates/common/consensus/beacon/src/electra/beacon_block.rs @@ -59,7 +59,7 @@ impl SignedBeaconBlock { kzg_commitment_inclusion_proof: self .message .body - .blob_kzg_commitment_inclusion_proof(index)? + .blob_kzg_commitment_inclusion_proof()? .try_into().map_err(|err| anyhow!("Failed to convert blob_kzg_commitment_inclusion_proof to FixedVector: {err:?}"))?, }) } diff --git a/crates/common/consensus/beacon/src/electra/beacon_block_body.rs b/crates/common/consensus/beacon/src/electra/beacon_block_body.rs index 32785490b..4464fb987 100644 --- a/crates/common/consensus/beacon/src/electra/beacon_block_body.rs +++ b/crates/common/consensus/beacon/src/electra/beacon_block_body.rs @@ -3,7 +3,6 @@ use ream_bls::BLSSignature; use ream_consensus_misc::{ constants::beacon::{ BLOB_KZG_COMMITMENTS_INDEX, BLOCK_BODY_MERKLE_DEPTH, EXECUTION_PAYLOAD_INDEX, - KZG_COMMITMENTS_MERKLE_DEPTH, }, deposit::Deposit, eth_1_data::Eth1Data, @@ -78,37 +77,8 @@ impl BeaconBlockBody { generate_proof(&tree, index, BLOCK_BODY_MERKLE_DEPTH) } - pub fn blob_kzg_commitment_inclusion_proof(&self, index: u64) -> anyhow::Result> { - // inclusion proof for blob_kzg_commitment in blob_kzg_commitments - let tree = merkle_tree( - self.blob_kzg_commitments - .iter() - .map(|commitment| commitment.tree_hash_root()) - .collect::>() - .as_slice(), - KZG_COMMITMENTS_MERKLE_DEPTH, - )?; - let kzg_commitment_to_kzg_commitments_proof = - generate_proof(&tree, index, KZG_COMMITMENTS_MERKLE_DEPTH)?; - - // add branch for length of blob_kzg_commitments - let kzg_commitments_length_root = self - .blob_kzg_commitments - .len() - .to_le_bytes() - .tree_hash_root(); - - // inclusion proof for blob_kzg_commitments in beacon_block_body - let kzg_commitments_to_block_body_proof = - self.data_inclusion_proof(BLOB_KZG_COMMITMENTS_INDEX)?; - - // merge proofs data - Ok([ - kzg_commitment_to_kzg_commitments_proof, - vec![kzg_commitments_length_root], - kzg_commitments_to_block_body_proof, - ] - .concat()) + pub fn blob_kzg_commitment_inclusion_proof(&self) -> anyhow::Result> { + self.data_inclusion_proof(BLOB_KZG_COMMITMENTS_INDEX) } pub fn execution_payload_inclusion_proof(&self) -> anyhow::Result> {