From 388a7cd05b535258fea2134ade12665758ff7efa Mon Sep 17 00:00:00 2001 From: geemo Date: Tue, 7 Feb 2023 14:58:12 -0600 Subject: [PATCH 01/22] rebase and add comment --- beacon_node/http_api/src/lib.rs | 93 +++++++++++++++++++++++++++++ beacon_node/http_api/tests/tests.rs | 51 +++++++++++++++- common/eth2/src/lib.rs | 34 +++++++++++ lighthouse/src/main.rs | 2 +- 4 files changed, 178 insertions(+), 2 deletions(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 973be2d49b4..78cf5c02dd8 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -1726,6 +1726,97 @@ pub fn serve( * beacon/rewards */ + let beacon_light_client_path = eth_v1 + .and(warp::path("beacon")) + .and(warp::path("light_client")) + .and(chain_filter.clone()); + + // GET beacon/light_client/optimistic_update + let get_beacon_light_client_optimistic_update = beacon_light_client_path + .clone() + .and(warp::path("optimistic_update")) + .and(warp::path::end()) + .and(warp::header::optional::("accept")) + .and_then( + |chain: Arc>, accept_header: Option| { + blocking_task(move || { + let update = chain + .latest_seen_optimistic_update + .lock() + .clone() + .ok_or_else(|| { + warp_utils::reject::custom_not_found(format!( + "No LightClientOptimisticUpdate is available" + )) + })?; + + let fork_name = chain + .spec + .fork_name_at_slot::(update.signature_slot); + match accept_header { + Some(api_types::Accept::Ssz) => Response::builder() + .status(200) + .header("Content-Type", "application/octet-stream") + .body(update.as_ssz_bytes().into()) + .map_err(|e| { + warp_utils::reject::custom_server_error(format!( + "failed to create response: {}", + e + )) + }), + _ => Ok(warp::reply::json(&api_types::GenericResponse::from(update)) + .into_response()), + } + .map(|resp| add_consensus_version_header(resp, fork_name)) + }) + }, + ); + + // GET beacon/light_client/finality_update + let get_beacon_light_client_finality_update = beacon_light_client_path + .clone() + .and(warp::path("finality_update")) + .and(warp::path::end()) + .and(warp::header::optional::("accept")) + .and_then( + |chain: Arc>, accept_header: Option| { + blocking_task(move || { + let update = chain + .latest_seen_finality_update + .lock() + .clone() + .ok_or_else(|| { + warp_utils::reject::custom_not_found(format!( + "No LightClientFinalityUpdate is available" + )) + })?; + + let fork_name = chain + .spec + .fork_name_at_slot::(update.signature_slot); + match accept_header { + Some(api_types::Accept::Ssz) => Response::builder() + .status(200) + .header("Content-Type", "application/octet-stream") + .body(update.as_ssz_bytes().into()) + .map_err(|e| { + warp_utils::reject::custom_server_error(format!( + "failed to create response: {}", + e + )) + }), + _ => Ok(warp::reply::json(&api_types::GenericResponse::from(update)) + .into_response()), + } + .map(|resp| add_consensus_version_header(resp, fork_name)) + }) + }, + ); + + /* + * beacon/rewards + */ + let beacon_rewards_path = eth_v1 .and(warp::path("beacon")) .and(warp::path("rewards")) @@ -3456,6 +3547,8 @@ pub fn serve( .or(get_beacon_pool_voluntary_exits.boxed()) .or(get_beacon_deposit_snapshot.boxed()) .or(get_beacon_rewards_blocks.boxed()) + .or(get_beacon_light_client_optimistic_update.boxed()) + .or(get_beacon_light_client_finality_update.boxed()) .or(get_config_fork_schedule.boxed()) .or(get_config_spec.boxed()) .or(get_config_deposit_contract.boxed()) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 2e795e522d5..ab2b347fced 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -31,7 +31,7 @@ use tree_hash::TreeHash; use types::application_domain::ApplicationDomain; use types::{ AggregateSignature, BitList, Domain, EthSpec, ExecutionBlockHash, Hash256, Keypair, - MainnetEthSpec, RelativeEpoch, SelectionProof, SignedRoot, Slot, + LightClientOptimisticUpdate, MainnetEthSpec, RelativeEpoch, SelectionProof, SignedRoot, Slot, }; type E = MainnetEthSpec; @@ -1202,6 +1202,39 @@ impl ApiTester { self } + pub async fn test_get_beacon_light_client_optimistic_update(self) -> Self { + // get_beacon_light_client_optimistic_update returns Ok(None) on 404 NOT FOUND + let result = match self + .client + .get_beacon_light_client_optimistic_update::() + .await + { + Ok(result) => result, + Err(_) => panic!("query did not fail correctly"), + }; + + let expected = self.chain.latest_seen_optimistic_update.lock().clone(); + assert_eq!(result, expected); + + self + } + + pub async fn test_get_beacon_light_client_finality_update(self) -> Self { + let result = match self + .client + .get_beacon_light_client_finality_update::() + .await + { + Ok(result) => result, + Err(_) => panic!("query did not fail correctly"), + }; + + let expected = self.chain.latest_seen_finality_update.lock().clone(); + assert_eq!(result, expected); + + self + } + pub async fn test_get_beacon_pool_attestations(self) -> Self { let result = self .client @@ -3947,6 +3980,22 @@ async fn node_get() { .await; } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn get_light_client_optimistic_update() { + ApiTester::new() + .await + .test_get_beacon_light_client_optimistic_update() + .await; +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn get_light_client_finality_update() { + ApiTester::new() + .await + .test_get_beacon_light_client_finality_update() + .await; +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn get_validator_duties_early() { ApiTester::new() diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 653c6c0bcc7..77f7088e722 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -562,6 +562,40 @@ impl BeaconNodeHttpClient { self.get_opt(path).await } + /// `GET beacon/light_client/optimimistic_update` + /// + /// Returns `Ok(None)` on a 404 error. + pub async fn get_beacon_light_client_optimistic_update( + &self, + ) -> Result>, Error> { + let mut path = self.eth_path(V1)?; + + path.path_segments_mut() + .map_err(|()| Error::InvalidUrl(self.server.clone()))? + .push("beacon") + .push("light_client") + .push("optimistic_update"); + + self.get_opt(path).await + } + + /// `GET beacon/light_client/finality_update` + /// + /// Returns `Ok(None)` on a 404 error. + pub async fn get_beacon_light_client_finality_update( + &self, + ) -> Result>, Error> { + let mut path = self.eth_path(V1)?; + + path.path_segments_mut() + .map_err(|()| Error::InvalidUrl(self.server.clone()))? + .push("beacon") + .push("light_client") + .push("finality_update"); + + self.get_opt(path).await + } + /// `GET beacon/headers?slot,parent_root` /// /// Returns `Ok(None)` on a 404 error. diff --git a/lighthouse/src/main.rs b/lighthouse/src/main.rs index babe2f8dca7..d9ad22decbd 100644 --- a/lighthouse/src/main.rs +++ b/lighthouse/src/main.rs @@ -1,4 +1,4 @@ -#![recursion_limit = "256"] +#![recursion_limit = "512"] mod metrics; From b47b4f25c258c858473d12b0be2bf7b2ce9a8bd6 Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 12:36:31 -0600 Subject: [PATCH 02/22] conditional test --- beacon_node/client/src/builder.rs | 1 + beacon_node/http_api/src/lib.rs | 20 ++++++++++++++++++-- beacon_node/http_api/tests/common.rs | 1 + beacon_node/http_api/tests/main.rs | 2 +- beacon_node/http_api/tests/tests.rs | 2 +- 5 files changed, 22 insertions(+), 4 deletions(-) diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index 3b016ebda9c..a1eecbc9814 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -147,6 +147,7 @@ where } else { None }; + self.http_api_config.enable_light_client_server = config.network.enable_light_client_server; let execution_layer = if let Some(config) = config.execution_layer { let context = runtime_context.service_context("exec".into()); diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 78cf5c02dd8..b4aaccbb67b 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -118,6 +118,7 @@ pub struct Config { pub allow_sync_stalled: bool, pub spec_fork_name: Option, pub data_dir: PathBuf, + pub enable_light_client_server: bool, } impl Default for Config { @@ -131,6 +132,7 @@ impl Default for Config { allow_sync_stalled: false, spec_fork_name: None, data_dir: PathBuf::from(DEFAULT_ROOT_DIR), + enable_light_client_server: false, } } } @@ -251,6 +253,18 @@ pub fn prometheus_metrics() -> warp::filters::log::Log impl Filter + Clone { + warp::any() + .and_then(move || async move { + if is_enabled { + Ok(()) + } else { + Err(warp::reject::not_found()) + } + }) + .untuple_one() +} + /// Creates a server that will serve requests using information from `ctx`. /// /// The server will shut down gracefully when the `shutdown` future resolves. @@ -3547,8 +3561,6 @@ pub fn serve( .or(get_beacon_pool_voluntary_exits.boxed()) .or(get_beacon_deposit_snapshot.boxed()) .or(get_beacon_rewards_blocks.boxed()) - .or(get_beacon_light_client_optimistic_update.boxed()) - .or(get_beacon_light_client_finality_update.boxed()) .or(get_config_fork_schedule.boxed()) .or(get_config_spec.boxed()) .or(get_config_deposit_contract.boxed()) @@ -3591,6 +3603,10 @@ pub fn serve( .recover(warp_utils::reject::handle_rejection), ) .boxed() + .or(enable(true) + .and(get_beacon_light_client_optimistic_update.boxed()) + ) + .boxed() .or(warp::post().and( post_beacon_blocks .boxed() diff --git a/beacon_node/http_api/tests/common.rs b/beacon_node/http_api/tests/common.rs index 7c228d9803f..ee85db8fc44 100644 --- a/beacon_node/http_api/tests/common.rs +++ b/beacon_node/http_api/tests/common.rs @@ -176,6 +176,7 @@ pub async fn create_api_server_on_port( allow_sync_stalled: false, data_dir: std::path::PathBuf::from(DEFAULT_ROOT_DIR), spec_fork_name: None, + enable_light_client_server: true, }, chain: Some(chain.clone()), network_senders: Some(network_senders), diff --git a/beacon_node/http_api/tests/main.rs b/beacon_node/http_api/tests/main.rs index ca6a27530a6..76a851dc4d2 100644 --- a/beacon_node/http_api/tests/main.rs +++ b/beacon_node/http_api/tests/main.rs @@ -1,5 +1,5 @@ #![cfg(not(debug_assertions))] // Tests are too slow in debug. -#![recursion_limit = "256"] +#![recursion_limit = "512"] pub mod common; pub mod fork_tests; diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index ab2b347fced..4058160771f 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -1210,7 +1210,7 @@ impl ApiTester { .await { Ok(result) => result, - Err(_) => panic!("query did not fail correctly"), + Err(e) => panic!("query did not fail correctly"), }; let expected = self.chain.latest_seen_optimistic_update.lock().clone(); From df78885f93e05a3986341c8e778f133652c26c44 Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 13:18:29 -0600 Subject: [PATCH 03/22] test --- beacon_node/http_api/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index b4aaccbb67b..409b068e0ac 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -3604,7 +3604,7 @@ pub fn serve( ) .boxed() .or(enable(true) - .and(get_beacon_light_client_optimistic_update.boxed()) + .and(get_beacon_light_client_optimistic_update) ) .boxed() .or(warp::post().and( From e124c995220d6181802d3cdf1a36799d4751ab0e Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 13:47:46 -0600 Subject: [PATCH 04/22] optimistic chould be working now --- beacon_node/http_api/src/lib.rs | 7 +++---- beacon_node/http_api/tests/tests.rs | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 409b068e0ac..51c459b3545 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -3597,16 +3597,15 @@ pub fn serve( .or(get_lighthouse_database_info.boxed()) .or(get_lighthouse_block_rewards.boxed()) .or(get_lighthouse_attestation_performance.boxed()) + .or(enable(ctx.config.enable_light_client_server) + .and(get_beacon_light_client_optimistic_update.boxed()) + ) .or(get_lighthouse_block_packing_efficiency.boxed()) .or(get_lighthouse_merge_readiness.boxed()) .or(get_events.boxed()) .recover(warp_utils::reject::handle_rejection), ) .boxed() - .or(enable(true) - .and(get_beacon_light_client_optimistic_update) - ) - .boxed() .or(warp::post().and( post_beacon_blocks .boxed() diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 4058160771f..945847b7365 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -31,7 +31,7 @@ use tree_hash::TreeHash; use types::application_domain::ApplicationDomain; use types::{ AggregateSignature, BitList, Domain, EthSpec, ExecutionBlockHash, Hash256, Keypair, - LightClientOptimisticUpdate, MainnetEthSpec, RelativeEpoch, SelectionProof, SignedRoot, Slot, + MainnetEthSpec, RelativeEpoch, SelectionProof, SignedRoot, Slot, }; type E = MainnetEthSpec; From cfdeca0650fcf195132ef5e9f597a6325add198a Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 14:00:01 -0600 Subject: [PATCH 05/22] finality should be working now --- beacon_node/http_api/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 51c459b3545..e56d63b395b 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -3599,6 +3599,7 @@ pub fn serve( .or(get_lighthouse_attestation_performance.boxed()) .or(enable(ctx.config.enable_light_client_server) .and(get_beacon_light_client_optimistic_update.boxed()) + .or(get_beacon_light_client_finality_update.boxed()) ) .or(get_lighthouse_block_packing_efficiency.boxed()) .or(get_lighthouse_merge_readiness.boxed()) From e15b98f4709484478ab062885f73faafdb0193f4 Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 14:08:15 -0600 Subject: [PATCH 06/22] try again --- beacon_node/http_api/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index e56d63b395b..b3fb75ec475 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -3599,7 +3599,7 @@ pub fn serve( .or(get_lighthouse_attestation_performance.boxed()) .or(enable(ctx.config.enable_light_client_server) .and(get_beacon_light_client_optimistic_update.boxed()) - .or(get_beacon_light_client_finality_update.boxed()) + .and(get_beacon_light_client_finality_update.boxed()) ) .or(get_lighthouse_block_packing_efficiency.boxed()) .or(get_lighthouse_merge_readiness.boxed()) From 6db8a414b3c4ca834bff9dc1f6670cd6e10472a5 Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 14:10:09 -0600 Subject: [PATCH 07/22] try again --- beacon_node/http_api/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index b3fb75ec475..d0123c5be03 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -3599,6 +3599,8 @@ pub fn serve( .or(get_lighthouse_attestation_performance.boxed()) .or(enable(ctx.config.enable_light_client_server) .and(get_beacon_light_client_optimistic_update.boxed()) + ) + .or(enable(ctx.config.enable_light_client_server) .and(get_beacon_light_client_finality_update.boxed()) ) .or(get_lighthouse_block_packing_efficiency.boxed()) From 1537947ee582b3560cba61e8866ef78708d1580d Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 14:30:40 -0600 Subject: [PATCH 08/22] clippy fix --- beacon_node/http_api/src/lib.rs | 18 ++++++++---------- beacon_node/http_api/tests/tests.rs | 2 +- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index d0123c5be03..27f5c998904 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -1759,9 +1759,9 @@ pub fn serve( .lock() .clone() .ok_or_else(|| { - warp_utils::reject::custom_not_found(format!( - "No LightClientOptimisticUpdate is available" - )) + warp_utils::reject::custom_not_found( + "No LightClientOptimisticUpdate is available".to_string(), + ) })?; let fork_name = chain @@ -1800,9 +1800,9 @@ pub fn serve( .lock() .clone() .ok_or_else(|| { - warp_utils::reject::custom_not_found(format!( - "No LightClientFinalityUpdate is available" - )) + warp_utils::reject::custom_not_found( + "No LightClientFinalityUpdate is available".to_string(), + ) })?; let fork_name = chain @@ -3598,11 +3598,9 @@ pub fn serve( .or(get_lighthouse_block_rewards.boxed()) .or(get_lighthouse_attestation_performance.boxed()) .or(enable(ctx.config.enable_light_client_server) - .and(get_beacon_light_client_optimistic_update.boxed()) - ) + .and(get_beacon_light_client_optimistic_update.boxed())) .or(enable(ctx.config.enable_light_client_server) - .and(get_beacon_light_client_finality_update.boxed()) - ) + .and(get_beacon_light_client_finality_update.boxed())) .or(get_lighthouse_block_packing_efficiency.boxed()) .or(get_lighthouse_merge_readiness.boxed()) .or(get_events.boxed()) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 945847b7365..5960d1bfb9a 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -1210,7 +1210,7 @@ impl ApiTester { .await { Ok(result) => result, - Err(e) => panic!("query did not fail correctly"), + Err(_) => panic!("query did not fail correctly"), }; let expected = self.chain.latest_seen_optimistic_update.lock().clone(); From 5dbde26a64b99fd437f5fbeee6ca80d4f5cb90c6 Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 19:57:16 -0600 Subject: [PATCH 09/22] add lc bootstrap beacon api --- beacon_node/http_api/src/lib.rs | 71 ++++++++++++++++++- .../src/rpc/codec/ssz_snappy.rs | 2 +- .../lighthouse_network/src/rpc/methods.rs | 2 +- .../src/service/api_types.rs | 2 +- .../beacon_processor/worker/rpc_methods.rs | 2 +- consensus/types/src/lib.rs | 1 + 6 files changed, 75 insertions(+), 5 deletions(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 27f5c998904..091d81b79d3 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -58,7 +58,7 @@ use types::{ ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof, SignedBeaconBlock, SignedBlindedBeaconBlock, SignedContributionAndProof, SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncCommitteeMessage, - SyncContributionData, + SyncContributionData, Hash256, LightClientBootstrap, }; use version::{ add_consensus_version_header, execution_optimistic_fork_versioned_response, @@ -1745,6 +1745,73 @@ pub fn serve( .and(warp::path("light_client")) .and(chain_filter.clone()); + // GET beacon/light_client/bootrap/{block_root} + let get_beacon_light_client_bootstrap = beacon_light_client_path + .clone() + .and(warp::path("bootstrap")) + .and(warp::path::param::().or_else(|_| async { + Err(warp_utils::reject::custom_bad_request( + "Invalid block root value".to_string(), + )) + })) + .and(warp::path::end()) + .and(warp::header::optional::("accept")) + .and_then( + |chain: Arc>, + block_root: Hash256, + accept_header: Option| { + blocking_task(move || { + let state_root = chain + .get_blinded_block(&block_root) + .map_err(|_| { + warp_utils::reject::custom_server_error( + "Error retrieving block".to_string(), + ) + })? + .map(|signed_block| signed_block.state_root()) + .ok_or_else(|| { + warp_utils::reject::custom_not_found( + "Light client bootstrap unavailable".to_string(), + ) + })?; + + let mut state = chain.get_state(&state_root, None).map_err(|_| { + warp_utils::reject::custom_server_error( + "Error retrieving state".to_string(), + ) + })? + .ok_or_else(|| { + warp_utils::reject::custom_not_found("Light client bootstrap unavailable".to_string()) + })?; + let fork_name = state + .fork_name(&chain.spec) + .map_err(inconsistent_fork_rejection)?; + let bootstrap = LightClientBootstrap::from_beacon_state(&mut state) + .map_err(|_| { + warp_utils::reject::custom_server_error( + "Failed to create light client bootstrap".to_string(), + ) + })?; + match accept_header { + Some(api_types::Accept::Ssz) => Response::builder() + .status(200) + .header("Content-Type", "application/octet-stream") + .body(bootstrap.as_ssz_bytes().into()) + .map_err(|e| { + warp_utils::reject::custom_server_error(format!( + "failed to create response: {}", + e + )) + }), + _ => Ok(warp::reply::json(&api_types::GenericResponse::from(bootstrap)) + .into_response()), + } + .map(|resp| add_consensus_version_header(resp, fork_name)) + }) + } + ); + + // GET beacon/light_client/optimistic_update let get_beacon_light_client_optimistic_update = beacon_light_client_path .clone() @@ -3601,6 +3668,8 @@ pub fn serve( .and(get_beacon_light_client_optimistic_update.boxed())) .or(enable(ctx.config.enable_light_client_server) .and(get_beacon_light_client_finality_update.boxed())) + .or(enable(ctx.config.enable_light_client_server) + .and(get_beacon_light_client_bootstrap.boxed())) .or(get_lighthouse_block_packing_efficiency.boxed()) .or(get_lighthouse_merge_readiness.boxed()) .or(get_events.boxed()) diff --git a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs index eccbf0dd623..96b0cd26eb6 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs @@ -16,7 +16,7 @@ use std::marker::PhantomData; use std::sync::Arc; use tokio_util::codec::{Decoder, Encoder}; use types::{ - light_client_bootstrap::LightClientBootstrap, EthSpec, ForkContext, ForkName, Hash256, + LightClientBootstrap, EthSpec, ForkContext, ForkName, Hash256, SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockMerge, }; use unsigned_varint::codec::Uvi; diff --git a/beacon_node/lighthouse_network/src/rpc/methods.rs b/beacon_node/lighthouse_network/src/rpc/methods.rs index 5da595c3db7..ed302fc475e 100644 --- a/beacon_node/lighthouse_network/src/rpc/methods.rs +++ b/beacon_node/lighthouse_network/src/rpc/methods.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use strum::IntoStaticStr; use superstruct::superstruct; use types::{ - light_client_bootstrap::LightClientBootstrap, Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot, + LightClientBootstrap, Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot, }; /// Maximum number of blocks in a single request. diff --git a/beacon_node/lighthouse_network/src/service/api_types.rs b/beacon_node/lighthouse_network/src/service/api_types.rs index 849a86f51ba..bd76b1beb13 100644 --- a/beacon_node/lighthouse_network/src/service/api_types.rs +++ b/beacon_node/lighthouse_network/src/service/api_types.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use libp2p::core::connection::ConnectionId; -use types::{light_client_bootstrap::LightClientBootstrap, EthSpec, SignedBeaconBlock}; +use types::{LightClientBootstrap, EthSpec, SignedBeaconBlock}; use crate::rpc::{ methods::{ diff --git a/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs b/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs index bfa0ea516fa..7523d584351 100644 --- a/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs +++ b/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs @@ -11,7 +11,7 @@ use slog::{debug, error}; use slot_clock::SlotClock; use std::sync::Arc; use task_executor::TaskExecutor; -use types::{light_client_bootstrap::LightClientBootstrap, Epoch, EthSpec, Hash256, Slot}; +use types::{LightClientBootstrap, Epoch, EthSpec, Hash256, Slot}; use super::Worker; diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 87f5ebe8b3c..c803c0e5192 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -139,6 +139,7 @@ pub use crate::free_attestation::FreeAttestation; pub use crate::graffiti::{Graffiti, GRAFFITI_BYTES_LEN}; pub use crate::historical_batch::HistoricalBatch; pub use crate::indexed_attestation::IndexedAttestation; +pub use crate::light_client_bootstrap::LightClientBootstrap; pub use crate::light_client_finality_update::LightClientFinalityUpdate; pub use crate::light_client_optimistic_update::LightClientOptimisticUpdate; pub use crate::participation_flags::ParticipationFlags; From 4611a9c21cf7cd73a62fda45edefc8c337eba0f1 Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 20:18:11 -0600 Subject: [PATCH 10/22] add lc optimistic/finality update to events --- beacon_node/beacon_chain/src/events.rs | 18 ++++++++++++++++++ beacon_node/http_api/src/lib.rs | 6 ++++++ common/eth2/src/types.rs | 26 ++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) diff --git a/beacon_node/beacon_chain/src/events.rs b/beacon_node/beacon_chain/src/events.rs index 6f4415ef4f3..a0075efd24e 100644 --- a/beacon_node/beacon_chain/src/events.rs +++ b/beacon_node/beacon_chain/src/events.rs @@ -15,6 +15,8 @@ pub struct ServerSentEventHandler { chain_reorg_tx: Sender>, contribution_tx: Sender>, late_head: Sender>, + light_client_finality_update_tx: Sender>, + light_client_optimistic_update_tx: Sender>, block_reward_tx: Sender>, log: Logger, } @@ -33,6 +35,8 @@ impl ServerSentEventHandler { let (chain_reorg_tx, _) = broadcast::channel(capacity); let (contribution_tx, _) = broadcast::channel(capacity); let (late_head, _) = broadcast::channel(capacity); + let (light_client_finality_update_tx, _) = broadcast::channel(capacity); + let (light_client_optimistic_update_tx, _) = broadcast::channel(capacity); let (block_reward_tx, _) = broadcast::channel(capacity); Self { @@ -44,6 +48,8 @@ impl ServerSentEventHandler { chain_reorg_tx, contribution_tx, late_head, + light_client_finality_update_tx, + light_client_optimistic_update_tx, block_reward_tx, log, } @@ -70,6 +76,10 @@ impl ServerSentEventHandler { .map(|count| trace!(self.log, "Registering server-sent contribution and proof event"; "receiver_count" => count)), EventKind::LateHead(late_head) => self.late_head.send(EventKind::LateHead(late_head)) .map(|count| trace!(self.log, "Registering server-sent late head event"; "receiver_count" => count)), + EventKind::LightClientFinalityUpdate(update) => self.light_client_finality_update_tx.send(EventKind::LightClientFinalityUpdate(update)) + .map(|count| trace!(self.log, "Registering server-sent light client finality update event"; "receiver_count" => count)), + EventKind::LightClientOptimisticUpdate(update) => self.light_client_optimistic_update_tx.send(EventKind::LightClientOptimisticUpdate(update)) + .map(|count| trace!(self.log, "Registering server-sent light client optimistic update event"; "receiver_count" => count)), EventKind::BlockReward(block_reward) => self.block_reward_tx.send(EventKind::BlockReward(block_reward)) .map(|count| trace!(self.log, "Registering server-sent contribution and proof event"; "receiver_count" => count)), }; @@ -110,6 +120,14 @@ impl ServerSentEventHandler { self.late_head.subscribe() } + pub fn subscribe_light_client_finality_update(&self) -> Receiver> { + self.light_client_finality_update_tx.subscribe() + } + + pub fn subscribe_light_client_optimistic_update(&self) -> Receiver> { + self.light_client_optimistic_update_tx.subscribe() + } + pub fn subscribe_block_reward(&self) -> Receiver> { self.block_reward_tx.subscribe() } diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 091d81b79d3..7df8a9c7e4f 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -3567,6 +3567,12 @@ pub fn serve( api_types::EventTopic::LateHead => { event_handler.subscribe_late_head() } + api_types::EventTopic::LightClientFinalityUpdate => { + event_handler.subscribe_light_client_finality_update() + } + api_types::EventTopic::LightClientOptimisticUpdate => { + event_handler.subscribe_light_client_optimistic_update() + } api_types::EventTopic::BlockReward => { event_handler.subscribe_block_reward() } diff --git a/common/eth2/src/types.rs b/common/eth2/src/types.rs index 53cca49120a..788cbc5309d 100644 --- a/common/eth2/src/types.rs +++ b/common/eth2/src/types.rs @@ -923,6 +923,8 @@ pub enum EventKind { ChainReorg(SseChainReorg), ContributionAndProof(Box>), LateHead(SseLateHead), + LightClientFinalityUpdate(Box>), + LightClientOptimisticUpdate(Box>), #[cfg(feature = "lighthouse")] BlockReward(BlockReward), } @@ -938,6 +940,8 @@ impl EventKind { EventKind::ChainReorg(_) => "chain_reorg", EventKind::ContributionAndProof(_) => "contribution_and_proof", EventKind::LateHead(_) => "late_head", + EventKind::LightClientFinalityUpdate(_) => "light_client_finality_update", + EventKind::LightClientOptimisticUpdate(_) => "light_client_optimistic_update", #[cfg(feature = "lighthouse")] EventKind::BlockReward(_) => "block_reward", } @@ -992,6 +996,22 @@ impl EventKind { ServerError::InvalidServerSentEvent(format!("Contribution and Proof: {:?}", e)) })?, ))), + "light_client_finality_update" => Ok(EventKind::LightClientFinalityUpdate( + serde_json::from_str(data).map_err(|e| { + ServerError::InvalidServerSentEvent(format!( + "Light Client Finality Update: {:?}", + e + )) + })?, + )), + "light_client_optimistic_update" => Ok(EventKind::LightClientOptimisticUpdate( + serde_json::from_str(data).map_err(|e| { + ServerError::InvalidServerSentEvent(format!( + "Light Client Optimistic Update: {:?}", + e + )) + })?, + )), #[cfg(feature = "lighthouse")] "block_reward" => Ok(EventKind::BlockReward(serde_json::from_str(data).map_err( |e| ServerError::InvalidServerSentEvent(format!("Block Reward: {:?}", e)), @@ -1021,6 +1041,8 @@ pub enum EventTopic { ChainReorg, ContributionAndProof, LateHead, + LightClientFinalityUpdate, + LightClientOptimisticUpdate, #[cfg(feature = "lighthouse")] BlockReward, } @@ -1038,6 +1060,8 @@ impl FromStr for EventTopic { "chain_reorg" => Ok(EventTopic::ChainReorg), "contribution_and_proof" => Ok(EventTopic::ContributionAndProof), "late_head" => Ok(EventTopic::LateHead), + "light_client_finality_update" => Ok(EventTopic::LightClientFinalityUpdate), + "light_client_optimistic_update" => Ok(EventTopic::LightClientOptimisticUpdate), #[cfg(feature = "lighthouse")] "block_reward" => Ok(EventTopic::BlockReward), _ => Err("event topic cannot be parsed.".to_string()), @@ -1056,6 +1080,8 @@ impl fmt::Display for EventTopic { EventTopic::ChainReorg => write!(f, "chain_reorg"), EventTopic::ContributionAndProof => write!(f, "contribution_and_proof"), EventTopic::LateHead => write!(f, "late_head"), + EventTopic::LightClientFinalityUpdate => write!(f, "light_client_finality_update"), + EventTopic::LightClientOptimisticUpdate => write!(f, "light_client_optimistic_update"), #[cfg(feature = "lighthouse")] EventTopic::BlockReward => write!(f, "block_reward"), } From d3da99fa748a6eb50e8f76701d5f090a72721494 Mon Sep 17 00:00:00 2001 From: geemo Date: Wed, 8 Feb 2023 20:30:06 -0600 Subject: [PATCH 11/22] fmt --- beacon_node/http_api/src/lib.rs | 49 ++++++++++--------- .../src/rpc/codec/ssz_snappy.rs | 4 +- .../lighthouse_network/src/rpc/methods.rs | 4 +- .../src/service/api_types.rs | 2 +- .../beacon_processor/worker/rpc_methods.rs | 2 +- 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 7df8a9c7e4f..698e5715f71 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -54,11 +54,11 @@ use tokio::sync::mpsc::{Sender, UnboundedSender}; use tokio_stream::{wrappers::BroadcastStream, StreamExt}; use types::{ Attestation, AttestationData, AttesterSlashing, BeaconStateError, BlindedPayload, - CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, FullPayload, - ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof, - SignedBeaconBlock, SignedBlindedBeaconBlock, SignedContributionAndProof, - SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncCommitteeMessage, - SyncContributionData, Hash256, LightClientBootstrap, + CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, FullPayload, Hash256, + LightClientBootstrap, ProposerPreparationData, ProposerSlashing, RelativeEpoch, + SignedAggregateAndProof, SignedBeaconBlock, SignedBlindedBeaconBlock, + SignedContributionAndProof, SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, + SyncCommitteeMessage, SyncContributionData, }; use version::{ add_consensus_version_header, execution_optimistic_fork_versioned_response, @@ -1758,8 +1758,8 @@ pub fn serve( .and(warp::header::optional::("accept")) .and_then( |chain: Arc>, - block_root: Hash256, - accept_header: Option| { + block_root: Hash256, + accept_header: Option| { blocking_task(move || { let state_root = chain .get_blinded_block(&block_root) @@ -1775,19 +1775,23 @@ pub fn serve( ) })?; - let mut state = chain.get_state(&state_root, None).map_err(|_| { - warp_utils::reject::custom_server_error( - "Error retrieving state".to_string(), - ) - })? - .ok_or_else(|| { - warp_utils::reject::custom_not_found("Light client bootstrap unavailable".to_string()) - })?; + let mut state = chain + .get_state(&state_root, None) + .map_err(|_| { + warp_utils::reject::custom_server_error( + "Error retrieving state".to_string(), + ) + })? + .ok_or_else(|| { + warp_utils::reject::custom_not_found( + "Light client bootstrap unavailable".to_string(), + ) + })?; let fork_name = state .fork_name(&chain.spec) .map_err(inconsistent_fork_rejection)?; - let bootstrap = LightClientBootstrap::from_beacon_state(&mut state) - .map_err(|_| { + let bootstrap = + LightClientBootstrap::from_beacon_state(&mut state).map_err(|_| { warp_utils::reject::custom_server_error( "Failed to create light client bootstrap".to_string(), ) @@ -1803,15 +1807,16 @@ pub fn serve( e )) }), - _ => Ok(warp::reply::json(&api_types::GenericResponse::from(bootstrap)) - .into_response()), + _ => Ok( + warp::reply::json(&api_types::GenericResponse::from(bootstrap)) + .into_response(), + ), } .map(|resp| add_consensus_version_header(resp, fork_name)) - }) - } + }) + }, ); - // GET beacon/light_client/optimistic_update let get_beacon_light_client_optimistic_update = beacon_light_client_path .clone() diff --git a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs index 96b0cd26eb6..1481e38c6a7 100644 --- a/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs +++ b/beacon_node/lighthouse_network/src/rpc/codec/ssz_snappy.rs @@ -16,8 +16,8 @@ use std::marker::PhantomData; use std::sync::Arc; use tokio_util::codec::{Decoder, Encoder}; use types::{ - LightClientBootstrap, EthSpec, ForkContext, ForkName, Hash256, - SignedBeaconBlock, SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockMerge, + EthSpec, ForkContext, ForkName, Hash256, LightClientBootstrap, SignedBeaconBlock, + SignedBeaconBlockAltair, SignedBeaconBlockBase, SignedBeaconBlockMerge, }; use unsigned_varint::codec::Uvi; diff --git a/beacon_node/lighthouse_network/src/rpc/methods.rs b/beacon_node/lighthouse_network/src/rpc/methods.rs index ed302fc475e..47963c7000d 100644 --- a/beacon_node/lighthouse_network/src/rpc/methods.rs +++ b/beacon_node/lighthouse_network/src/rpc/methods.rs @@ -12,9 +12,7 @@ use std::ops::Deref; use std::sync::Arc; use strum::IntoStaticStr; use superstruct::superstruct; -use types::{ - LightClientBootstrap, Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot, -}; +use types::{Epoch, EthSpec, Hash256, LightClientBootstrap, SignedBeaconBlock, Slot}; /// Maximum number of blocks in a single request. pub type MaxRequestBlocks = U1024; diff --git a/beacon_node/lighthouse_network/src/service/api_types.rs b/beacon_node/lighthouse_network/src/service/api_types.rs index bd76b1beb13..cc524ea4b60 100644 --- a/beacon_node/lighthouse_network/src/service/api_types.rs +++ b/beacon_node/lighthouse_network/src/service/api_types.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use libp2p::core::connection::ConnectionId; -use types::{LightClientBootstrap, EthSpec, SignedBeaconBlock}; +use types::{EthSpec, LightClientBootstrap, SignedBeaconBlock}; use crate::rpc::{ methods::{ diff --git a/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs b/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs index 7523d584351..b490ff0170e 100644 --- a/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs +++ b/beacon_node/network/src/beacon_processor/worker/rpc_methods.rs @@ -11,7 +11,7 @@ use slog::{debug, error}; use slot_clock::SlotClock; use std::sync::Arc; use task_executor::TaskExecutor; -use types::{LightClientBootstrap, Epoch, EthSpec, Hash256, Slot}; +use types::{Epoch, EthSpec, Hash256, LightClientBootstrap, Slot}; use super::Worker; From a2f6a8ac52a7aaf50894bec3ada0466a5df23870 Mon Sep 17 00:00:00 2001 From: geemo Date: Fri, 10 Feb 2023 23:53:41 -0600 Subject: [PATCH 12/22] That error isn't occuring on my computer but I think this should fix it --- beacon_node/tests/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/beacon_node/tests/test.rs b/beacon_node/tests/test.rs index 1c11a8349dd..8ccb260d29b 100644 --- a/beacon_node/tests/test.rs +++ b/beacon_node/tests/test.rs @@ -1,5 +1,5 @@ #![cfg(test)] -#![recursion_limit = "256"] +#![recursion_limit = "512"] use beacon_chain::StateSkipConfig; use node_test_rig::{ From d74e3b46b179e55034e28b1a0364ddd7fcd4cc5a Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Thu, 16 Nov 2023 12:05:05 +1100 Subject: [PATCH 13/22] Add missing test file --- .../fixtures/mainnet/test_blobs_bundle.ssz | Bin 0 -> 131180 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 beacon_node/execution_layer/src/test_utils/fixtures/mainnet/test_blobs_bundle.ssz diff --git a/beacon_node/execution_layer/src/test_utils/fixtures/mainnet/test_blobs_bundle.ssz b/beacon_node/execution_layer/src/test_utils/fixtures/mainnet/test_blobs_bundle.ssz new file mode 100644 index 0000000000000000000000000000000000000000..6b549a4da84f33f1feea50ab073da9078c18bd6d GIT binary patch literal 131180 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�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 Date: Thu, 16 Nov 2023 16:34:02 +1100 Subject: [PATCH 14/22] Update light client types to comply with Altair light client spec. --- ...ght_client_finality_update_verification.rs | 4 +- ...t_client_optimistic_update_verification.rs | 5 ++- .../lighthouse_network/src/rpc/methods.rs | 6 ++- common/eth2/src/lib.rs | 29 +++++++++++--- .../state_processing/src/upgrade/altair.rs | 2 +- consensus/types/src/lib.rs | 2 + consensus/types/src/light_client_bootstrap.rs | 34 +++++++++++++--- .../types/src/light_client_finality_update.rs | 39 +++++++++++++++---- consensus/types/src/light_client_header.rs | 26 +++++++++++++ .../src/light_client_optimistic_update.rs | 30 ++++++++++++-- consensus/types/src/light_client_update.rs | 34 +++++++++++++--- consensus/types/src/sync_committee.rs | 12 ++---- 12 files changed, 180 insertions(+), 43 deletions(-) create mode 100644 consensus/types/src/light_client_header.rs diff --git a/beacon_node/beacon_chain/src/light_client_finality_update_verification.rs b/beacon_node/beacon_chain/src/light_client_finality_update_verification.rs index 86cc6695edb..791d63ccfe5 100644 --- a/beacon_node/beacon_chain/src/light_client_finality_update_verification.rs +++ b/beacon_node/beacon_chain/src/light_client_finality_update_verification.rs @@ -67,7 +67,7 @@ impl VerifiedLightClientFinalityUpdate { chain: &BeaconChain, seen_timestamp: Duration, ) -> Result { - let gossiped_finality_slot = light_client_finality_update.finalized_header.slot; + let gossiped_finality_slot = light_client_finality_update.finalized_header.beacon.slot; let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0); let signature_slot = light_client_finality_update.signature_slot; let start_time = chain.slot_clock.start_of(signature_slot); @@ -88,7 +88,7 @@ impl VerifiedLightClientFinalityUpdate { .get_blinded_block(&finalized_block_root)? .ok_or(Error::FailedConstructingUpdate)?; let latest_seen_finality_update_slot = match latest_seen_finality_update.as_ref() { - Some(update) => update.finalized_header.slot, + Some(update) => update.finalized_header.beacon.slot, None => Slot::new(0), }; diff --git a/beacon_node/beacon_chain/src/light_client_optimistic_update_verification.rs b/beacon_node/beacon_chain/src/light_client_optimistic_update_verification.rs index 8b7e6445273..374cc9a7753 100644 --- a/beacon_node/beacon_chain/src/light_client_optimistic_update_verification.rs +++ b/beacon_node/beacon_chain/src/light_client_optimistic_update_verification.rs @@ -71,7 +71,7 @@ impl VerifiedLightClientOptimisticUpdate { chain: &BeaconChain, seen_timestamp: Duration, ) -> Result { - let gossiped_optimistic_slot = light_client_optimistic_update.attested_header.slot; + let gossiped_optimistic_slot = light_client_optimistic_update.attested_header.beacon.slot; let one_third_slot_duration = Duration::new(chain.spec.seconds_per_slot / 3, 0); let signature_slot = light_client_optimistic_update.signature_slot; let start_time = chain.slot_clock.start_of(signature_slot); @@ -88,7 +88,7 @@ impl VerifiedLightClientOptimisticUpdate { .get_state(&attested_block.state_root(), Some(attested_block.slot()))? .ok_or(Error::FailedConstructingUpdate)?; let latest_seen_optimistic_update_slot = match latest_seen_optimistic_update.as_ref() { - Some(update) => update.attested_header.slot, + Some(update) => update.attested_header.beacon.slot, None => Slot::new(0), }; @@ -114,6 +114,7 @@ impl VerifiedLightClientOptimisticUpdate { // otherwise queue let canonical_root = light_client_optimistic_update .attested_header + .beacon .canonical_root(); if canonical_root != head_block.message().parent_root() { diff --git a/beacon_node/lighthouse_network/src/rpc/methods.rs b/beacon_node/lighthouse_network/src/rpc/methods.rs index a293de4e9bd..627c871c471 100644 --- a/beacon_node/lighthouse_network/src/rpc/methods.rs +++ b/beacon_node/lighthouse_network/src/rpc/methods.rs @@ -571,7 +571,11 @@ impl std::fmt::Display for RPCResponse { RPCResponse::Pong(ping) => write!(f, "Pong: {}", ping.data), RPCResponse::MetaData(metadata) => write!(f, "Metadata: {}", metadata.seq_number()), RPCResponse::LightClientBootstrap(bootstrap) => { - write!(f, "LightClientBootstrap Slot: {}", bootstrap.header.slot) + write!( + f, + "LightClientBootstrap Slot: {}", + bootstrap.header.beacon.slot + ) } } } diff --git a/common/eth2/src/lib.rs b/common/eth2/src/lib.rs index 9436d303ac6..7ed1c5c540c 100644 --- a/common/eth2/src/lib.rs +++ b/common/eth2/src/lib.rs @@ -667,12 +667,31 @@ impl BeaconNodeHttpClient { self.get_opt(path).await } - /// `GET beacon/light_client/optimimistic_update` + /// `GET beacon/light_client/bootstrap` /// /// Returns `Ok(None)` on a 404 error. - pub async fn get_beacon_light_client_optimistic_update( + pub async fn get_light_client_bootstrap( &self, - ) -> Result>, Error> { + block_root: Hash256, + ) -> Result>>, Error> { + let mut path = self.eth_path(V1)?; + + path.path_segments_mut() + .map_err(|()| Error::InvalidUrl(self.server.clone()))? + .push("beacon") + .push("light_client") + .push("bootstrap") + .push(&format!("{:?}", block_root)); + + self.get_opt(path).await + } + + /// `GET beacon/light_client/optimistic_update` + /// + /// Returns `Ok(None)` on a 404 error. + pub async fn get_beacon_light_client_optimistic_update( + &self, + ) -> Result>>, Error> { let mut path = self.eth_path(V1)?; path.path_segments_mut() @@ -687,9 +706,9 @@ impl BeaconNodeHttpClient { /// `GET beacon/light_client/finality_update` /// /// Returns `Ok(None)` on a 404 error. - pub async fn get_beacon_light_client_finality_update( + pub async fn get_beacon_light_client_finality_update( &self, - ) -> Result>, Error> { + ) -> Result>>, Error> { let mut path = self.eth_path(V1)?; path.path_segments_mut() diff --git a/consensus/state_processing/src/upgrade/altair.rs b/consensus/state_processing/src/upgrade/altair.rs index 26b1192bc16..5bb4f0bd592 100644 --- a/consensus/state_processing/src/upgrade/altair.rs +++ b/consensus/state_processing/src/upgrade/altair.rs @@ -54,7 +54,7 @@ pub fn upgrade_to_altair( VariableList::new(vec![ParticipationFlags::default(); pre.validators.len()])?; let inactivity_scores = VariableList::new(vec![0; pre.validators.len()])?; - let temp_sync_committee = Arc::new(SyncCommittee::temporary()?); + let temp_sync_committee = Arc::new(SyncCommittee::temporary()); // Where possible, use something like `mem::take` to move fields from behind the &mut // reference. For other fields that don't have a good default value, use `clone`. diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 52300df3fd4..262cd24f9a8 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -99,6 +99,7 @@ pub mod slot_data; pub mod sqlite; pub mod blob_sidecar; +pub mod light_client_header; pub mod sidecar; pub mod signed_blob; @@ -156,6 +157,7 @@ pub use crate::historical_batch::HistoricalBatch; pub use crate::indexed_attestation::IndexedAttestation; pub use crate::light_client_bootstrap::LightClientBootstrap; pub use crate::light_client_finality_update::LightClientFinalityUpdate; +pub use crate::light_client_header::LightClientHeader; pub use crate::light_client_optimistic_update::LightClientOptimisticUpdate; pub use crate::participation_flags::ParticipationFlags; pub use crate::participation_list::ParticipationList; diff --git a/consensus/types/src/light_client_bootstrap.rs b/consensus/types/src/light_client_bootstrap.rs index 1d70456d732..15cc564c6e7 100644 --- a/consensus/types/src/light_client_bootstrap.rs +++ b/consensus/types/src/light_client_bootstrap.rs @@ -1,6 +1,10 @@ -use super::{BeaconBlockHeader, BeaconState, EthSpec, FixedVector, Hash256, SyncCommittee}; -use crate::{light_client_update::*, test_utils::TestRandom}; -use serde::{Deserialize, Serialize}; +use super::{BeaconState, EthSpec, FixedVector, Hash256, SyncCommittee}; +use crate::{ + light_client_update::*, test_utils::TestRandom, ForkName, ForkVersionDeserialize, + LightClientHeader, +}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Value; use ssz_derive::{Decode, Encode}; use std::sync::Arc; use test_random_derive::TestRandom; @@ -22,8 +26,8 @@ use tree_hash::TreeHash; #[serde(bound = "T: EthSpec")] #[arbitrary(bound = "T: EthSpec")] pub struct LightClientBootstrap { - /// Requested beacon block header. - pub header: BeaconBlockHeader, + /// The requested beacon block header. + pub header: LightClientHeader, /// The `SyncCommittee` used in the requested period. pub current_sync_committee: Arc>, /// Merkle proof for sync committee @@ -37,13 +41,31 @@ impl LightClientBootstrap { let current_sync_committee_branch = beacon_state.compute_merkle_proof(CURRENT_SYNC_COMMITTEE_INDEX)?; Ok(LightClientBootstrap { - header, + header: header.into(), current_sync_committee: beacon_state.current_sync_committee()?.clone(), current_sync_committee_branch: FixedVector::new(current_sync_committee_branch)?, }) } } +impl ForkVersionDeserialize for LightClientBootstrap { + fn deserialize_by_fork<'de, D: Deserializer<'de>>( + value: Value, + fork_name: ForkName, + ) -> Result { + match fork_name { + ForkName::Altair => Ok(serde_json::from_value::>(value) + .map_err(serde::de::Error::custom))?, + ForkName::Base | ForkName::Merge | ForkName::Capella | ForkName::Deneb => { + Err(serde::de::Error::custom(format!( + "LightClientBootstrap failed to deserialize: unsupported fork '{}'", + fork_name + ))) + } + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/consensus/types/src/light_client_finality_update.rs b/consensus/types/src/light_client_finality_update.rs index 30f337fb2bb..49c2db28b90 100644 --- a/consensus/types/src/light_client_finality_update.rs +++ b/consensus/types/src/light_client_finality_update.rs @@ -1,9 +1,12 @@ use super::{ - BeaconBlockHeader, EthSpec, FixedVector, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, - Slot, SyncAggregate, + EthSpec, FixedVector, Hash256, SignedBeaconBlock, SignedBlindedBeaconBlock, Slot, SyncAggregate, }; -use crate::{light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec}; -use serde::{Deserialize, Serialize}; +use crate::{ + light_client_update::*, test_utils::TestRandom, BeaconState, ChainSpec, ForkName, + ForkVersionDeserialize, LightClientHeader, +}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Value; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash::TreeHash; @@ -25,9 +28,9 @@ use tree_hash::TreeHash; #[arbitrary(bound = "T: EthSpec")] pub struct LightClientFinalityUpdate { /// The last `BeaconBlockHeader` from the last attested block by the sync committee. - pub attested_header: BeaconBlockHeader, + pub attested_header: LightClientHeader, /// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch). - pub finalized_header: BeaconBlockHeader, + pub finalized_header: LightClientHeader, /// Merkle proof attesting finalized header. pub finality_branch: FixedVector, /// current sync aggreggate @@ -68,8 +71,8 @@ impl LightClientFinalityUpdate { let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?; Ok(Self { - attested_header, - finalized_header, + attested_header: attested_header.into(), + finalized_header: finalized_header.into(), finality_branch: FixedVector::new(finality_branch)?, sync_aggregate: sync_aggregate.clone(), signature_slot: block.slot(), @@ -77,6 +80,26 @@ impl LightClientFinalityUpdate { } } +impl ForkVersionDeserialize for LightClientFinalityUpdate { + fn deserialize_by_fork<'de, D: Deserializer<'de>>( + value: Value, + fork_name: ForkName, + ) -> Result { + match fork_name { + ForkName::Altair => Ok( + serde_json::from_value::>(value) + .map_err(serde::de::Error::custom), + )?, + ForkName::Base | ForkName::Merge | ForkName::Capella | ForkName::Deneb => { + Err(serde::de::Error::custom(format!( + "LightClientFinalityUpdate failed to deserialize: unsupported fork '{}'", + fork_name + ))) + } + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/consensus/types/src/light_client_header.rs b/consensus/types/src/light_client_header.rs new file mode 100644 index 00000000000..8fe31f7af8c --- /dev/null +++ b/consensus/types/src/light_client_header.rs @@ -0,0 +1,26 @@ +use crate::test_utils::TestRandom; +use crate::BeaconBlockHeader; +use serde::{Deserialize, Serialize}; +use ssz_derive::{Decode, Encode}; +use test_random_derive::TestRandom; + +#[derive( + Debug, + Clone, + PartialEq, + Serialize, + Deserialize, + Encode, + Decode, + TestRandom, + arbitrary::Arbitrary, +)] +pub struct LightClientHeader { + pub beacon: BeaconBlockHeader, +} + +impl From for LightClientHeader { + fn from(beacon: BeaconBlockHeader) -> Self { + LightClientHeader { beacon } + } +} diff --git a/consensus/types/src/light_client_optimistic_update.rs b/consensus/types/src/light_client_optimistic_update.rs index fbb0558eced..3a0f1d536c8 100644 --- a/consensus/types/src/light_client_optimistic_update.rs +++ b/consensus/types/src/light_client_optimistic_update.rs @@ -1,8 +1,10 @@ -use super::{BeaconBlockHeader, EthSpec, Slot, SyncAggregate}; +use super::{EthSpec, ForkName, ForkVersionDeserialize, Slot, SyncAggregate}; +use crate::light_client_header::LightClientHeader; use crate::{ light_client_update::Error, test_utils::TestRandom, BeaconState, ChainSpec, SignedBeaconBlock, }; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Value; use ssz_derive::{Decode, Encode}; use test_random_derive::TestRandom; use tree_hash::TreeHash; @@ -24,7 +26,7 @@ use tree_hash::TreeHash; #[arbitrary(bound = "T: EthSpec")] pub struct LightClientOptimisticUpdate { /// The last `BeaconBlockHeader` from the last attested block by the sync committee. - pub attested_header: BeaconBlockHeader, + pub attested_header: LightClientHeader, /// current sync aggreggate pub sync_aggregate: SyncAggregate, /// Slot of the sync aggregated singature @@ -53,13 +55,33 @@ impl LightClientOptimisticUpdate { let mut attested_header = attested_state.latest_block_header().clone(); attested_header.state_root = attested_state.tree_hash_root(); Ok(Self { - attested_header, + attested_header: attested_header.into(), sync_aggregate: sync_aggregate.clone(), signature_slot: block.slot(), }) } } +impl ForkVersionDeserialize for LightClientOptimisticUpdate { + fn deserialize_by_fork<'de, D: Deserializer<'de>>( + value: Value, + fork_name: ForkName, + ) -> Result { + match fork_name { + ForkName::Altair => Ok(serde_json::from_value::>( + value, + ) + .map_err(serde::de::Error::custom))?, + ForkName::Base | ForkName::Merge | ForkName::Capella | ForkName::Deneb => { + Err(serde::de::Error::custom(format!( + "LightClientOptimisticUpdate failed to deserialize: unsupported fork '{}'", + fork_name + ))) + } + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/consensus/types/src/light_client_update.rs b/consensus/types/src/light_client_update.rs index 6e53e14c994..6fb98427267 100644 --- a/consensus/types/src/light_client_update.rs +++ b/consensus/types/src/light_client_update.rs @@ -1,7 +1,11 @@ use super::{BeaconBlockHeader, EthSpec, FixedVector, Hash256, Slot, SyncAggregate, SyncCommittee}; -use crate::{beacon_state, test_utils::TestRandom, BeaconBlock, BeaconState, ChainSpec}; +use crate::{ + beacon_state, test_utils::TestRandom, BeaconBlock, BeaconState, ChainSpec, ForkName, + ForkVersionDeserialize, LightClientHeader, +}; use safe_arith::ArithError; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; +use serde_json::Value; use ssz_derive::{Decode, Encode}; use ssz_types::typenum::{U5, U6}; use std::sync::Arc; @@ -67,13 +71,13 @@ impl From for Error { #[arbitrary(bound = "T: EthSpec")] pub struct LightClientUpdate { /// The last `BeaconBlockHeader` from the last attested block by the sync committee. - pub attested_header: BeaconBlockHeader, + pub attested_header: LightClientHeader, /// The `SyncCommittee` used in the next period. pub next_sync_committee: Arc>, /// Merkle proof for next sync committee pub next_sync_committee_branch: FixedVector, /// The last `BeaconBlockHeader` from the last attested finalized block (end of epoch). - pub finalized_header: BeaconBlockHeader, + pub finalized_header: LightClientHeader, /// Merkle proof attesting finalized header. pub finality_branch: FixedVector, /// current sync aggreggate @@ -128,10 +132,10 @@ impl LightClientUpdate { attested_state.compute_merkle_proof(NEXT_SYNC_COMMITTEE_INDEX)?; let finality_branch = attested_state.compute_merkle_proof(FINALIZED_ROOT_INDEX)?; Ok(Self { - attested_header, + attested_header: attested_header.into(), next_sync_committee: attested_state.next_sync_committee()?.clone(), next_sync_committee_branch: FixedVector::new(next_sync_committee_branch)?, - finalized_header, + finalized_header: finalized_header.into(), finality_branch: FixedVector::new(finality_branch)?, sync_aggregate: sync_aggregate.clone(), signature_slot: block.slot(), @@ -139,6 +143,24 @@ impl LightClientUpdate { } } +impl ForkVersionDeserialize for LightClientUpdate { + fn deserialize_by_fork<'de, D: Deserializer<'de>>( + value: Value, + fork_name: ForkName, + ) -> Result { + match fork_name { + ForkName::Altair => Ok(serde_json::from_value::>(value) + .map_err(serde::de::Error::custom))?, + ForkName::Base | ForkName::Merge | ForkName::Capella | ForkName::Deneb => { + Err(serde::de::Error::custom(format!( + "LightClientUpdate failed to deserialize: unsupported fork '{}'", + fork_name + ))) + } + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/consensus/types/src/sync_committee.rs b/consensus/types/src/sync_committee.rs index 0bcf505f257..b42a000bb00 100644 --- a/consensus/types/src/sync_committee.rs +++ b/consensus/types/src/sync_committee.rs @@ -1,5 +1,4 @@ use crate::test_utils::TestRandom; -use crate::typenum::Unsigned; use crate::{EthSpec, FixedVector, SyncSubnetId}; use bls::PublicKeyBytes; use safe_arith::{ArithError, SafeArith}; @@ -46,14 +45,11 @@ pub struct SyncCommittee { impl SyncCommittee { /// Create a temporary sync committee that should *never* be included in a legitimate consensus object. - pub fn temporary() -> Result { - Ok(Self { - pubkeys: FixedVector::new(vec![ - PublicKeyBytes::empty(); - T::SyncCommitteeSize::to_usize() - ])?, + pub fn temporary() -> Self { + Self { + pubkeys: FixedVector::from_elem(PublicKeyBytes::empty()), aggregate_pubkey: PublicKeyBytes::empty(), - }) + } } /// Return the pubkeys in this `SyncCommittee` for the given `subcommittee_index`. From f062e774acc412f18e881b61c0d52b688dc545b7 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Thu, 16 Nov 2023 21:52:43 +1100 Subject: [PATCH 15/22] Fix test compilation --- beacon_node/http_api/tests/tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 5e5212b0cbc..8e049c5e387 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -1651,7 +1651,7 @@ impl ApiTester { .get_beacon_light_client_optimistic_update::() .await { - Ok(result) => result, + Ok(result) => result.map(|res| res.data), Err(_) => panic!("query did not fail correctly"), }; @@ -1667,7 +1667,7 @@ impl ApiTester { .get_beacon_light_client_finality_update::() .await { - Ok(result) => result, + Ok(result) => result.map(|res| res.data), Err(_) => panic!("query did not fail correctly"), }; From bfd3fb7b312aa7f1f63cdc8ab22ec2181fd0a26f Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Fri, 17 Nov 2023 13:32:38 +1100 Subject: [PATCH 16/22] Support deserializing light client structures for the Bellatrix fork --- consensus/types/src/light_client_bootstrap.rs | 8 +++++--- consensus/types/src/light_client_finality_update.rs | 10 +++++----- consensus/types/src/light_client_optimistic_update.rs | 8 ++++---- consensus/types/src/light_client_update.rs | 8 +++++--- 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/consensus/types/src/light_client_bootstrap.rs b/consensus/types/src/light_client_bootstrap.rs index 15cc564c6e7..14bed794633 100644 --- a/consensus/types/src/light_client_bootstrap.rs +++ b/consensus/types/src/light_client_bootstrap.rs @@ -54,9 +54,11 @@ impl ForkVersionDeserialize for LightClientBootstrap { fork_name: ForkName, ) -> Result { match fork_name { - ForkName::Altair => Ok(serde_json::from_value::>(value) - .map_err(serde::de::Error::custom))?, - ForkName::Base | ForkName::Merge | ForkName::Capella | ForkName::Deneb => { + ForkName::Altair | ForkName::Merge => { + Ok(serde_json::from_value::>(value) + .map_err(serde::de::Error::custom))? + } + ForkName::Base | ForkName::Capella | ForkName::Deneb => { Err(serde::de::Error::custom(format!( "LightClientBootstrap failed to deserialize: unsupported fork '{}'", fork_name diff --git a/consensus/types/src/light_client_finality_update.rs b/consensus/types/src/light_client_finality_update.rs index 49c2db28b90..87601b81565 100644 --- a/consensus/types/src/light_client_finality_update.rs +++ b/consensus/types/src/light_client_finality_update.rs @@ -86,11 +86,11 @@ impl ForkVersionDeserialize for LightClientFinalityUpdate { fork_name: ForkName, ) -> Result { match fork_name { - ForkName::Altair => Ok( - serde_json::from_value::>(value) - .map_err(serde::de::Error::custom), - )?, - ForkName::Base | ForkName::Merge | ForkName::Capella | ForkName::Deneb => { + ForkName::Altair | ForkName::Merge => Ok(serde_json::from_value::< + LightClientFinalityUpdate, + >(value) + .map_err(serde::de::Error::custom))?, + ForkName::Base | ForkName::Capella | ForkName::Deneb => { Err(serde::de::Error::custom(format!( "LightClientFinalityUpdate failed to deserialize: unsupported fork '{}'", fork_name diff --git a/consensus/types/src/light_client_optimistic_update.rs b/consensus/types/src/light_client_optimistic_update.rs index 3a0f1d536c8..d883d735f35 100644 --- a/consensus/types/src/light_client_optimistic_update.rs +++ b/consensus/types/src/light_client_optimistic_update.rs @@ -68,11 +68,11 @@ impl ForkVersionDeserialize for LightClientOptimisticUpdate { fork_name: ForkName, ) -> Result { match fork_name { - ForkName::Altair => Ok(serde_json::from_value::>( - value, - ) + ForkName::Altair | ForkName::Merge => Ok(serde_json::from_value::< + LightClientOptimisticUpdate, + >(value) .map_err(serde::de::Error::custom))?, - ForkName::Base | ForkName::Merge | ForkName::Capella | ForkName::Deneb => { + ForkName::Base | ForkName::Capella | ForkName::Deneb => { Err(serde::de::Error::custom(format!( "LightClientOptimisticUpdate failed to deserialize: unsupported fork '{}'", fork_name diff --git a/consensus/types/src/light_client_update.rs b/consensus/types/src/light_client_update.rs index 6fb98427267..718cd7553f9 100644 --- a/consensus/types/src/light_client_update.rs +++ b/consensus/types/src/light_client_update.rs @@ -149,9 +149,11 @@ impl ForkVersionDeserialize for LightClientUpdate { fork_name: ForkName, ) -> Result { match fork_name { - ForkName::Altair => Ok(serde_json::from_value::>(value) - .map_err(serde::de::Error::custom))?, - ForkName::Base | ForkName::Merge | ForkName::Capella | ForkName::Deneb => { + ForkName::Altair | ForkName::Merge => { + Ok(serde_json::from_value::>(value) + .map_err(serde::de::Error::custom))? + } + ForkName::Base | ForkName::Capella | ForkName::Deneb => { Err(serde::de::Error::custom(format!( "LightClientUpdate failed to deserialize: unsupported fork '{}'", fork_name From d90df3f103fe68e9b49d7a81fd04a70e4b3519c1 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Fri, 17 Nov 2023 15:02:13 +1100 Subject: [PATCH 17/22] Move `get_light_client_bootstrap` logic to `BeaconChain`. `LightClientBootstrap` API to return `ForkVersionedResponse`. --- beacon_node/beacon_chain/src/beacon_chain.rs | 27 +++++++ beacon_node/beacon_chain/src/errors.rs | 1 + beacon_node/http_api/src/lib.rs | 59 +++++---------- .../network_beacon_processor/rpc_methods.rs | 74 +++++-------------- consensus/types/src/lib.rs | 1 + 5 files changed, 68 insertions(+), 94 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 2fd70056cc1..c08a1e227fb 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -6446,6 +6446,33 @@ impl BeaconChain { pub fn data_availability_boundary(&self) -> Option { self.data_availability_checker.data_availability_boundary() } + + /// Gets the `LightClientBootstrap` object for a requested block root. + /// + /// Returns `None` when the state or block is not found in the database. + pub fn get_light_client_bootstrap( + &self, + block_root: &Hash256, + ) -> Result, ForkName)>, Error> { + let Some(state_root) = self + .get_blinded_block(block_root)? + .map(|block| block.state_root()) + else { + return Ok(None); + }; + + let Some(mut state) = self.get_state(&state_root, None)? else { + return Ok(None); + }; + + let fork_name = state + .fork_name(&self.spec) + .map_err(Error::InconsistentFork)?; + + LightClientBootstrap::from_beacon_state(&mut state) + .map(|bootstrap| Some((bootstrap, fork_name))) + .map_err(Error::LightClientError) + } } impl Drop for BeaconChain { diff --git a/beacon_node/beacon_chain/src/errors.rs b/beacon_node/beacon_chain/src/errors.rs index 7c1bb04917d..8e6018c72b0 100644 --- a/beacon_node/beacon_chain/src/errors.rs +++ b/beacon_node/beacon_chain/src/errors.rs @@ -221,6 +221,7 @@ pub enum BeaconChainError { ProposerHeadForkChoiceError(fork_choice::Error), UnableToPublish, AvailabilityCheckError(AvailabilityCheckError), + LightClientError(LightClientError), } easy_from_to!(SlotProcessingError, BeaconChainError); diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 63af4309981..11984bb88d0 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -76,8 +76,8 @@ use tokio_stream::{ }; use types::{ Attestation, AttestationData, AttestationShufflingId, AttesterSlashing, BeaconStateError, - BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, Hash256, - LightClientBootstrap, ProposerPreparationData, ProposerSlashing, RelativeEpoch, + BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName, + ForkVersionedResponse, Hash256, ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof, SignedBlsToExecutionChange, SignedContributionAndProof, SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncCommitteeMessage, SyncContributionData, @@ -2423,41 +2423,21 @@ pub fn serve( block_root: Hash256, accept_header: Option| { task_spawner.blocking_response_task(Priority::P1, move || { - let state_root = chain - .get_blinded_block(&block_root) - .map_err(|_| { - warp_utils::reject::custom_server_error( - "Error retrieving block".to_string(), - ) - })? - .map(|signed_block| signed_block.state_root()) - .ok_or_else(|| { - warp_utils::reject::custom_not_found( + let (bootstrap, fork_name) = match chain.get_light_client_bootstrap(&block_root) + { + Ok(Some(res)) => res, + Ok(None) => { + return Err(warp_utils::reject::custom_not_found( "Light client bootstrap unavailable".to_string(), - ) - })?; + )); + } + Err(e) => { + return Err(warp_utils::reject::custom_server_error(format!( + "Unable to obtain LightClientBootstrap instance: {e:?}" + ))); + } + }; - let mut state = chain - .get_state(&state_root, None) - .map_err(|_| { - warp_utils::reject::custom_server_error( - "Error retrieving state".to_string(), - ) - })? - .ok_or_else(|| { - warp_utils::reject::custom_not_found( - "Light client bootstrap unavailable".to_string(), - ) - })?; - let fork_name = state - .fork_name(&chain.spec) - .map_err(inconsistent_fork_rejection)?; - let bootstrap = - LightClientBootstrap::from_beacon_state(&mut state).map_err(|_| { - warp_utils::reject::custom_server_error( - "Failed to create light client bootstrap".to_string(), - ) - })?; match accept_header { Some(api_types::Accept::Ssz) => Response::builder() .status(200) @@ -2469,10 +2449,11 @@ pub fn serve( e )) }), - _ => Ok( - warp::reply::json(&api_types::GenericResponse::from(bootstrap)) - .into_response(), - ), + _ => Ok(warp::reply::json(&ForkVersionedResponse { + version: Some(fork_name), + data: bootstrap, + }) + .into_response()), } .map(|resp| add_consensus_version_header(resp, fork_name)) }) diff --git a/beacon_node/network/src/network_beacon_processor/rpc_methods.rs b/beacon_node/network/src/network_beacon_processor/rpc_methods.rs index 0e6c76e222b..7210e08c621 100644 --- a/beacon_node/network/src/network_beacon_processor/rpc_methods.rs +++ b/beacon_node/network/src/network_beacon_processor/rpc_methods.rs @@ -18,9 +18,7 @@ use std::sync::Arc; use task_executor::TaskExecutor; use tokio_stream::StreamExt; use types::blob_sidecar::BlobIdentifier; -use types::{ - light_client_bootstrap::LightClientBootstrap, Epoch, EthSpec, ForkName, Hash256, Slot, -}; +use types::{Epoch, EthSpec, ForkName, Hash256, Slot}; impl NetworkBeaconProcessor { /* Auxiliary functions */ @@ -304,66 +302,32 @@ impl NetworkBeaconProcessor { request: LightClientBootstrapRequest, ) { let block_root = request.root; - let state_root = match self.chain.get_blinded_block(&block_root) { - Ok(signed_block) => match signed_block { - Some(signed_block) => signed_block.state_root(), - None => { - self.send_error_response( - peer_id, - RPCResponseErrorCode::ResourceUnavailable, - "Bootstrap not available".into(), - request_id, - ); - return; - } - }, - Err(_) => { - self.send_error_response( - peer_id, - RPCResponseErrorCode::ResourceUnavailable, - "Bootstrap not available".into(), - request_id, - ); - return; - } - }; - let mut beacon_state = match self.chain.get_state(&state_root, None) { - Ok(beacon_state) => match beacon_state { - Some(state) => state, - None => { - self.send_error_response( - peer_id, - RPCResponseErrorCode::ResourceUnavailable, - "Bootstrap not available".into(), - request_id, - ); - return; - } - }, - Err(_) => { + match self.chain.get_light_client_bootstrap(&block_root) { + Ok(Some((bootstrap, _))) => self.send_response( + peer_id, + Response::LightClientBootstrap(bootstrap), + request_id, + ), + Ok(None) => self.send_error_response( + peer_id, + RPCResponseErrorCode::ResourceUnavailable, + "Bootstrap not available".into(), + request_id, + ), + Err(e) => { self.send_error_response( peer_id, RPCResponseErrorCode::ResourceUnavailable, "Bootstrap not available".into(), request_id, ); - return; + error!(self.log, "Error getting LightClientBootstrap instance"; + "block_root" => ?block_root, + "peer" => %peer_id, + "error" => ?e + ) } }; - let Ok(bootstrap) = LightClientBootstrap::from_beacon_state(&mut beacon_state) else { - self.send_error_response( - peer_id, - RPCResponseErrorCode::ResourceUnavailable, - "Bootstrap not available".into(), - request_id, - ); - return; - }; - self.send_response( - peer_id, - Response::LightClientBootstrap(bootstrap), - request_id, - ) } /// Handle a `BlocksByRange` request from the peer. diff --git a/consensus/types/src/lib.rs b/consensus/types/src/lib.rs index 262cd24f9a8..0f284bde9d2 100644 --- a/consensus/types/src/lib.rs +++ b/consensus/types/src/lib.rs @@ -159,6 +159,7 @@ pub use crate::light_client_bootstrap::LightClientBootstrap; pub use crate::light_client_finality_update::LightClientFinalityUpdate; pub use crate::light_client_header::LightClientHeader; pub use crate::light_client_optimistic_update::LightClientOptimisticUpdate; +pub use crate::light_client_update::{Error as LightClientError, LightClientUpdate}; pub use crate::participation_flags::ParticipationFlags; pub use crate::participation_list::ParticipationList; pub use crate::payload::{ From 80ff555de54a0726cfd5624d8fcb7e2436e462b7 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Sat, 18 Nov 2023 01:48:46 +1100 Subject: [PATCH 18/22] Misc fixes. - log cleanup - move http_api config mutation to `config::get_config` for consistency - fix light client API responses --- beacon_node/beacon_chain/src/events.rs | 14 ++------------ beacon_node/client/src/builder.rs | 1 - beacon_node/http_api/src/lib.rs | 18 ++++++++++++------ beacon_node/src/config.rs | 3 +++ lighthouse/tests/beacon_node.rs | 10 ++++++++-- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/beacon_node/beacon_chain/src/events.rs b/beacon_node/beacon_chain/src/events.rs index d52e763a410..0e5dfc80596 100644 --- a/beacon_node/beacon_chain/src/events.rs +++ b/beacon_node/beacon_chain/src/events.rs @@ -117,21 +117,11 @@ impl ServerSentEventHandler { EventKind::LightClientFinalityUpdate(_) => self .light_client_finality_update_tx .send(kind) - .map(|count| { - log_count( - "Registering server-sent light client finality update event", - count, - ) - }), + .map(|count| log_count("light client finality update", count)), EventKind::LightClientOptimisticUpdate(_) => self .light_client_optimistic_update_tx .send(kind) - .map(|count| { - log_count( - "Registering server-sent light client optimistic update event", - count, - ) - }), + .map(|count| log_count("light client optimistic update", count)), EventKind::BlockReward(_) => self .block_reward_tx .send(kind) diff --git a/beacon_node/client/src/builder.rs b/beacon_node/client/src/builder.rs index ce1ab0005d6..cedf347b9a8 100644 --- a/beacon_node/client/src/builder.rs +++ b/beacon_node/client/src/builder.rs @@ -165,7 +165,6 @@ where } else { None }; - self.http_api_config.enable_light_client_server = config.network.enable_light_client_server; let execution_layer = if let Some(config) = config.execution_layer.clone() { let context = runtime_context.service_context("exec".into()); diff --git a/beacon_node/http_api/src/lib.rs b/beacon_node/http_api/src/lib.rs index 11984bb88d0..5f2c641af97 100644 --- a/beacon_node/http_api/src/lib.rs +++ b/beacon_node/http_api/src/lib.rs @@ -2397,7 +2397,7 @@ pub fn serve( ); /* - * beacon/rewards + * beacon/light_client */ let beacon_light_client_path = eth_v1 @@ -2405,7 +2405,7 @@ pub fn serve( .and(warp::path("light_client")) .and(chain_filter.clone()); - // GET beacon/light_client/bootrap/{block_root} + // GET beacon/light_client/bootstrap/{block_root} let get_beacon_light_client_bootstrap = beacon_light_client_path .clone() .and(task_spawner_filter.clone()) @@ -2496,8 +2496,11 @@ pub fn serve( e )) }), - _ => Ok(warp::reply::json(&api_types::GenericResponse::from(update)) - .into_response()), + _ => Ok(warp::reply::json(&ForkVersionedResponse { + version: Some(fork_name), + data: update, + }) + .into_response()), } .map(|resp| add_consensus_version_header(resp, fork_name)) }) @@ -2540,8 +2543,11 @@ pub fn serve( e )) }), - _ => Ok(warp::reply::json(&api_types::GenericResponse::from(update)) - .into_response()), + _ => Ok(warp::reply::json(&ForkVersionedResponse { + version: Some(fork_name), + data: update, + }) + .into_response()), } .map(|resp| add_consensus_version_header(resp, fork_name)) }) diff --git a/beacon_node/src/config.rs b/beacon_node/src/config.rs index 609626ae88a..9ceab47f336 100644 --- a/beacon_node/src/config.rs +++ b/beacon_node/src/config.rs @@ -151,6 +151,9 @@ pub fn get_config( client_config.http_api.duplicate_block_status_code = parse_required(cli_args, "http-duplicate-block-status")?; + + client_config.http_api.enable_light_client_server = + cli_args.is_present("light-client-server"); } if let Some(cache_size) = clap_utils::parse_optional(cli_args, "shuffling-cache-size")? { diff --git a/lighthouse/tests/beacon_node.rs b/lighthouse/tests/beacon_node.rs index c8e064e224d..eb1341f9181 100644 --- a/lighthouse/tests/beacon_node.rs +++ b/lighthouse/tests/beacon_node.rs @@ -2346,7 +2346,10 @@ fn sync_eth1_chain_disable_deposit_contract_sync_flag() { fn light_client_server_default() { CommandLineTest::new() .run_with_zero_port() - .with_config(|config| assert_eq!(config.network.enable_light_client_server, false)); + .with_config(|config| { + assert_eq!(config.network.enable_light_client_server, false); + assert_eq!(config.http_api.enable_light_client_server, false); + }); } #[test] @@ -2354,7 +2357,10 @@ fn light_client_server_enabled() { CommandLineTest::new() .flag("light-client-server", None) .run_with_zero_port() - .with_config(|config| assert_eq!(config.network.enable_light_client_server, true)); + .with_config(|config| { + assert_eq!(config.network.enable_light_client_server, true); + assert_eq!(config.http_api.enable_light_client_server, true); + }); } #[test] From 75bd2ace39e4b944c8b2ce09bdc061e3ff05ceb9 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Sat, 18 Nov 2023 03:21:44 +1100 Subject: [PATCH 19/22] Add light client bootstrap API test and fix existing ones. --- beacon_node/beacon_chain/src/beacon_chain.rs | 11 +++-- beacon_node/beacon_chain/src/errors.rs | 1 + beacon_node/http_api/src/test_utils.rs | 1 + beacon_node/http_api/tests/tests.rs | 48 +++++++++++++++++-- consensus/types/src/light_client_bootstrap.rs | 3 +- 5 files changed, 55 insertions(+), 9 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index c08a1e227fb..dce2194a08d 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -6469,9 +6469,14 @@ impl BeaconChain { .fork_name(&self.spec) .map_err(Error::InconsistentFork)?; - LightClientBootstrap::from_beacon_state(&mut state) - .map(|bootstrap| Some((bootstrap, fork_name))) - .map_err(Error::LightClientError) + match fork_name { + ForkName::Altair | ForkName::Merge => { + LightClientBootstrap::from_beacon_state(&mut state) + .map(|bootstrap| Some((bootstrap, fork_name))) + .map_err(Error::LightClientError) + } + ForkName::Base | ForkName::Capella | ForkName::Deneb => Err(Error::UnsupportedFork), + } } } diff --git a/beacon_node/beacon_chain/src/errors.rs b/beacon_node/beacon_chain/src/errors.rs index 8e6018c72b0..9c1ba06f853 100644 --- a/beacon_node/beacon_chain/src/errors.rs +++ b/beacon_node/beacon_chain/src/errors.rs @@ -222,6 +222,7 @@ pub enum BeaconChainError { UnableToPublish, AvailabilityCheckError(AvailabilityCheckError), LightClientError(LightClientError), + UnsupportedFork, } easy_from_to!(SlotProcessingError, BeaconChainError); diff --git a/beacon_node/http_api/src/test_utils.rs b/beacon_node/http_api/src/test_utils.rs index fe47e56dc57..bafb5738194 100644 --- a/beacon_node/http_api/src/test_utils.rs +++ b/beacon_node/http_api/src/test_utils.rs @@ -209,6 +209,7 @@ pub async fn create_api_server( enabled: true, listen_port: port, data_dir: std::path::PathBuf::from(DEFAULT_ROOT_DIR), + enable_light_client_server: true, ..Config::default() }, chain: Some(chain), diff --git a/beacon_node/http_api/tests/tests.rs b/beacon_node/http_api/tests/tests.rs index 8e049c5e387..eaee6e6e352 100644 --- a/beacon_node/http_api/tests/tests.rs +++ b/beacon_node/http_api/tests/tests.rs @@ -1644,6 +1644,26 @@ impl ApiTester { self } + pub async fn test_get_beacon_light_client_bootstrap(self) -> Self { + let block_id = BlockId(CoreBlockId::Finalized); + let (block_root, _, _) = block_id.root(&self.chain).unwrap(); + let (block, _, _) = block_id.full_block(&self.chain).await.unwrap(); + + let result = match self + .client + .get_light_client_bootstrap::(block_root) + .await + { + Ok(result) => result.unwrap().data, + Err(e) => panic!("query failed incorrectly: {e:?}"), + }; + + let expected = block.slot(); + assert_eq!(result.header.beacon.slot, expected); + + self + } + pub async fn test_get_beacon_light_client_optimistic_update(self) -> Self { // get_beacon_light_client_optimistic_update returns Ok(None) on 404 NOT FOUND let result = match self @@ -1652,7 +1672,7 @@ impl ApiTester { .await { Ok(result) => result.map(|res| res.data), - Err(_) => panic!("query did not fail correctly"), + Err(e) => panic!("query failed incorrectly: {e:?}"), }; let expected = self.chain.latest_seen_optimistic_update.lock().clone(); @@ -1668,7 +1688,7 @@ impl ApiTester { .await { Ok(result) => result.map(|res| res.data), - Err(_) => panic!("query did not fail correctly"), + Err(e) => panic!("query failed incorrectly: {e:?}"), }; let expected = self.chain.latest_seen_finality_update.lock().clone(); @@ -5092,9 +5112,25 @@ async fn node_get() { .await; } +#[tokio::test(flavor = "multi_thread", worker_threads = 2)] +async fn get_light_client_bootstrap() { + let config = ApiTesterConfig { + spec: ForkName::Altair.make_genesis_spec(E::default_spec()), + ..<_>::default() + }; + ApiTester::new_from_config(config) + .await + .test_get_beacon_light_client_bootstrap() + .await; +} + #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn get_light_client_optimistic_update() { - ApiTester::new() + let config = ApiTesterConfig { + spec: ForkName::Altair.make_genesis_spec(E::default_spec()), + ..<_>::default() + }; + ApiTester::new_from_config(config) .await .test_get_beacon_light_client_optimistic_update() .await; @@ -5102,7 +5138,11 @@ async fn get_light_client_optimistic_update() { #[tokio::test(flavor = "multi_thread", worker_threads = 2)] async fn get_light_client_finality_update() { - ApiTester::new() + let config = ApiTesterConfig { + spec: ForkName::Altair.make_genesis_spec(E::default_spec()), + ..<_>::default() + }; + ApiTester::new_from_config(config) .await .test_get_beacon_light_client_finality_update() .await; diff --git a/consensus/types/src/light_client_bootstrap.rs b/consensus/types/src/light_client_bootstrap.rs index 14bed794633..616aced483a 100644 --- a/consensus/types/src/light_client_bootstrap.rs +++ b/consensus/types/src/light_client_bootstrap.rs @@ -8,7 +8,6 @@ use serde_json::Value; use ssz_derive::{Decode, Encode}; use std::sync::Arc; use test_random_derive::TestRandom; -use tree_hash::TreeHash; /// A LightClientBootstrap is the initializer we send over to lightclient nodes /// that are trying to generate their basic storage when booting up. @@ -37,7 +36,7 @@ pub struct LightClientBootstrap { impl LightClientBootstrap { pub fn from_beacon_state(beacon_state: &mut BeaconState) -> Result { let mut header = beacon_state.latest_block_header().clone(); - header.state_root = beacon_state.tree_hash_root(); + header.state_root = beacon_state.update_tree_hash_cache()?; let current_sync_committee_branch = beacon_state.compute_merkle_proof(CURRENT_SYNC_COMMITTEE_INDEX)?; Ok(LightClientBootstrap { From e0d0ece7ba2ece330dac7f6a70bebcf1390b4468 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Sat, 18 Nov 2023 03:57:51 +1100 Subject: [PATCH 20/22] Fix test for `light-client-server` http api config. --- lighthouse/tests/beacon_node.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lighthouse/tests/beacon_node.rs b/lighthouse/tests/beacon_node.rs index eb1341f9181..5f75cb1acff 100644 --- a/lighthouse/tests/beacon_node.rs +++ b/lighthouse/tests/beacon_node.rs @@ -2359,6 +2359,16 @@ fn light_client_server_enabled() { .run_with_zero_port() .with_config(|config| { assert_eq!(config.network.enable_light_client_server, true); + }); +} + +#[test] +fn light_client_http_server_enabled() { + CommandLineTest::new() + .flag("http", None) + .flag("light-client-server", None) + .run_with_zero_port() + .with_config(|config| { assert_eq!(config.http_api.enable_light_client_server, true); }); } From 161ece641c777146e3e7aa2fd459ec7c2194bd48 Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Sat, 18 Nov 2023 09:28:56 +1100 Subject: [PATCH 21/22] Appease clippy --- beacon_node/beacon_chain/src/beacon_chain.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 46f4fd8cbcd..4a2a308948f 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -6450,6 +6450,7 @@ impl BeaconChain { /// Gets the `LightClientBootstrap` object for a requested block root. /// /// Returns `None` when the state or block is not found in the database. + #[allow(clippy::type_complexity)] pub fn get_light_client_bootstrap( &self, block_root: &Hash256, From 9e1e126002253f825a033cd0b5b53700c703ffab Mon Sep 17 00:00:00 2001 From: Jimmy Chen Date: Tue, 28 Nov 2023 16:13:15 +1100 Subject: [PATCH 22/22] Efficiency improvement when retrieving beacon state. --- beacon_node/beacon_chain/src/beacon_chain.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/beacon_node/beacon_chain/src/beacon_chain.rs b/beacon_node/beacon_chain/src/beacon_chain.rs index 4a2a308948f..6b893d6967a 100644 --- a/beacon_node/beacon_chain/src/beacon_chain.rs +++ b/beacon_node/beacon_chain/src/beacon_chain.rs @@ -6455,14 +6455,14 @@ impl BeaconChain { &self, block_root: &Hash256, ) -> Result, ForkName)>, Error> { - let Some(state_root) = self + let Some((state_root, slot)) = self .get_blinded_block(block_root)? - .map(|block| block.state_root()) + .map(|block| (block.state_root(), block.slot())) else { return Ok(None); }; - let Some(mut state) = self.get_state(&state_root, None)? else { + let Some(mut state) = self.get_state(&state_root, Some(slot))? else { return Ok(None); };