Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 50 additions & 2 deletions bin/ethlambda/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,6 @@ async fn main() -> eyre::Result<()> {
ethlambda_blockchain::metrics::set_node_info("ethlambda", version::CLIENT_VERSION);
ethlambda_blockchain::metrics::set_node_start_time();

let node_p2p_key = read_hex_file_bytes(&options.node_key);
let p2p_socket = SocketAddr::new(IpAddr::from([0, 0, 0, 0]), options.gossipsub_port);
let rpc_config = RpcConfig {
http_address: options.http_address,
api_port: options.api_port,
Expand All @@ -120,6 +118,22 @@ async fn main() -> eyre::Result<()> {
println!("{ASCII_ART}");

info!(version = version::CLIENT_VERSION, "Starting ethlambda");

// Hive lean spec-asset suites boot the client with
// HIVE_LEAN_TEST_DRIVER=1 so it skips the consensus/p2p stack and
// exposes only the `/lean/v0/test_driver/...` endpoints driven by the
// simulator. Detected here before any config / key / genesis loading
// so the driver run doesn't touch --node-key, --custom-network-config-dir,
// or any other consensus prerequisite the hive shim doesn't bother to
// provision.
if ethlambda_rpc::test_driver::test_driver_enabled() {
info!("HIVE_LEAN_TEST_DRIVER detected; booting in test-driver mode");
return run_test_driver(rpc_config).await;
}

let node_p2p_key = read_hex_file_bytes(&options.node_key);
let p2p_socket = SocketAddr::new(IpAddr::from([0, 0, 0, 0]), options.gossipsub_port);

#[cfg(not(target_env = "msvc"))]
info!("Using jemalloc allocator with heap profiling enabled");
#[cfg(target_env = "msvc")]
Expand Down Expand Up @@ -275,6 +289,40 @@ async fn main() -> eyre::Result<()> {
Ok(())
}

/// Boot the binary in Hive test-driver mode.
///
/// Skips every consensus/p2p subsystem and just exposes the
/// `/lean/v0/test_driver/...` HTTP endpoints over the configured API port.
/// The driver-mode store is seeded with an empty in-memory state and is
/// replaced on every `fork_choice/init` request from the simulator.
async fn run_test_driver(rpc_config: RpcConfig) -> eyre::Result<()> {
use tokio::sync::RwLock;

let driver: ethlambda_rpc::test_driver::DriverState =
Arc::new(RwLock::new(ethlambda_rpc::test_driver::empty_driver_store()));

let shutdown_token = CancellationToken::new();
let rpc_shutdown = shutdown_token.clone();

let rpc_handle = tokio::spawn(async move {
if let Err(err) =
ethlambda_rpc::start_test_driver_rpc_server(rpc_config, driver, rpc_shutdown).await
{
error!(%err, "Test-driver RPC server failed");
}
});

info!("Test-driver RPC ready");

tokio::signal::ctrl_c().await.ok();
info!("Shutdown signal received, stopping test-driver RPC...");
shutdown_token.cancel();
let _ = rpc_handle.await;
info!("Shutdown complete");

Ok(())
}

/// Subset of `validator-config.yaml` consumed by ethlambda.
///
/// The `config` block is a network-wide settings bag shared across clients;
Expand Down
14 changes: 11 additions & 3 deletions crates/blockchain/src/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ fn on_block_core(
let sig_verification_start = std::time::Instant::now();
if verify {
// Validate cryptographic signatures
verify_signatures(&parent_state, &signed_block)?;
verify_block_signatures(&parent_state, &signed_block)?;
}
let sig_verification = sig_verification_start.elapsed();

Expand Down Expand Up @@ -1157,7 +1157,15 @@ fn build_block(
/// Verify all signatures in a signed block.
///
/// Each attestation has a corresponding proof in the signature list.
fn verify_signatures(state: &State, signed_block: &SignedBlock) -> Result<(), StoreError> {
///
/// Exposed publicly so RPC handlers (notably the Hive test-driver
/// `verify_signatures/run` endpoint) can run the exact same verification path
/// the import pipeline uses; the production import path also calls this from
/// [`on_block_core`].
pub fn verify_block_signatures(
state: &State,
signed_block: &SignedBlock,
) -> Result<(), StoreError> {
use ethlambda_crypto::verify_aggregated_signature;
use ethlambda_types::signature::ValidatorSignature;

Expand Down Expand Up @@ -1372,7 +1380,7 @@ mod tests {
},
};

let result = verify_signatures(&state, &signed_block);
let result = verify_block_signatures(&state, &signed_block);
assert!(
matches!(result, Err(StoreError::ParticipantsMismatch)),
"Expected ParticipantsMismatch, got: {result:?}"
Expand Down
3 changes: 0 additions & 3 deletions crates/blockchain/tests/common.rs

This file was deleted.

37 changes: 5 additions & 32 deletions crates/blockchain/tests/forkchoice_spectests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,16 @@ use std::{
use ethlambda_blockchain::{MILLISECONDS_PER_INTERVAL, MILLISECONDS_PER_SLOT, store};
use ethlambda_storage::{Store, backend::InMemoryBackend};
use ethlambda_types::{
attestation::{AttestationData, SignedAggregatedAttestation, SignedAttestation, XmssSignature},
block::{AggregatedSignatureProof, Block, BlockSignatures, SignedBlock},
attestation::{AttestationData, SignedAggregatedAttestation, SignedAttestation},
block::{AggregatedSignatureProof, Block},
primitives::{ByteList, H256, HashTreeRoot as _},
signature::SIGNATURE_SIZE,
state::State,
};

use crate::types::{ForkChoiceTestVector, StoreChecks};
use ethlambda_test_fixtures::fork_choice::{AttestationCheck, ForkChoiceTestVector, StoreChecks};

const SUPPORTED_FIXTURE_FORMAT: &str = "fork_choice_test";

mod common;
mod types;

/// List of skipped tests.
const SKIP_TESTS: &[&str] = &[];

Expand Down Expand Up @@ -66,7 +62,7 @@ fn run(path: &Path) -> datatest_stable::Result<()> {
block_registry.insert(label.clone(), root);
}

let signed_block = build_signed_block(block_data);
let signed_block = block_data.to_blank_signed_block();

let block_time_ms =
genesis_time * 1000 + signed_block.message.slot * MILLISECONDS_PER_SLOT;
Expand Down Expand Up @@ -161,29 +157,6 @@ fn assert_step_outcome<T, E: std::fmt::Debug>(
}
}

fn build_signed_block(block_data: types::BlockStepData) -> SignedBlock {
let block: Block = block_data.to_block();

// Build one empty proof per attestation, matching the aggregation_bits from
// each attestation in the block body. Block processing zips attestations with
// signatures, so they must be the same length for attestations to reach
// fork choice.
let proofs: Vec<_> = block
.body
.attestations
.iter()
.map(|att| AggregatedSignatureProof::empty(att.aggregation_bits.clone()))
.collect();

SignedBlock {
message: block,
signature: BlockSignatures {
proposer_signature: XmssSignature::try_from(vec![0u8; SIGNATURE_SIZE]).unwrap(),
attestation_signatures: proofs.try_into().expect("attestation proofs within limit"),
},
}
}

fn validate_checks(
st: &Store,
checks: &StoreChecks,
Expand Down Expand Up @@ -374,7 +347,7 @@ fn validate_checks(

fn validate_attestation_check(
st: &Store,
check: &types::AttestationCheck,
check: &AttestationCheck,
step_idx: usize,
) -> datatest_stable::Result<()> {
let validator_id = check.validator;
Expand Down
4 changes: 1 addition & 3 deletions crates/blockchain/tests/signature_spectests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ use ethlambda_types::{
state::State,
};

mod common;
mod signature_types;
use signature_types::VerifySignaturesTestVector;
use ethlambda_test_fixtures::verify_signatures::VerifySignaturesTestVector;

const SUPPORTED_FIXTURE_FORMAT: &str = "verify_signatures_test";

Expand Down
112 changes: 0 additions & 112 deletions crates/blockchain/tests/signature_types.rs

This file was deleted.

1 change: 1 addition & 0 deletions crates/common/test-fixtures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ ethlambda-types.workspace = true
libssz-types.workspace = true

serde.workspace = true
serde_json.workspace = true
hex.workspace = true
Loading