API for LightClientBootstrap, LightClientFinalityUpdate, LightClientOptimisticUpdate and light client events (#3954)

* rebase and add comment

* conditional test

* test

* optimistic chould be working now

* finality should be working now

* try again

* try again

* clippy fix

* add lc bootstrap beacon api

* add lc optimistic/finality update to events

* fmt

* That error isn't occuring on my computer but I think this should fix it

* Add missing test file

* Update light client types to comply with Altair light client spec.

* Fix test compilation

* Support deserializing light client structures for the Bellatrix fork

* Move `get_light_client_bootstrap` logic to `BeaconChain`. `LightClientBootstrap` API to return `ForkVersionedResponse`.

* Misc fixes.
- log cleanup
- move http_api config mutation to `config::get_config` for consistency
- fix light client API responses

* Add light client bootstrap API test and fix existing ones.

* Fix test for `light-client-server` http api config.

* Appease clippy

* Efficiency improvement when retrieving beacon state.

---------

Co-authored-by: Jimmy Chen <jchen.tc@gmail.com>
This commit is contained in:
GeemoCandama
2023-11-28 00:14:29 -06:00
committed by GitHub
parent 44c1817c2b
commit 8a599ec7dc
24 changed files with 632 additions and 111 deletions

View File

@@ -77,9 +77,10 @@ use tokio_stream::{
use types::{
Attestation, AttestationData, AttestationShufflingId, AttesterSlashing, BeaconStateError,
BlindedPayload, CommitteeCache, ConfigAndPreset, Epoch, EthSpec, ForkName,
ProposerPreparationData, ProposerSlashing, RelativeEpoch, SignedAggregateAndProof,
SignedBlsToExecutionChange, SignedContributionAndProof, SignedValidatorRegistrationData,
SignedVoluntaryExit, Slot, SyncCommitteeMessage, SyncContributionData,
ForkVersionedResponse, Hash256, ProposerPreparationData, ProposerSlashing, RelativeEpoch,
SignedAggregateAndProof, SignedBlsToExecutionChange, SignedContributionAndProof,
SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncCommitteeMessage,
SyncContributionData,
};
use validator::pubkey_to_validator_index;
use version::{
@@ -143,6 +144,7 @@ pub struct Config {
pub enable_beacon_processor: bool,
#[serde(with = "eth2::types::serde_status_code")]
pub duplicate_block_status_code: StatusCode,
pub enable_light_client_server: bool,
}
impl Default for Config {
@@ -159,6 +161,7 @@ impl Default for Config {
sse_capacity_multiplier: 1,
enable_beacon_processor: true,
duplicate_block_status_code: StatusCode::ACCEPTED,
enable_light_client_server: false,
}
}
}
@@ -279,6 +282,18 @@ pub fn prometheus_metrics() -> warp::filters::log::Log<impl Fn(warp::filters::lo
})
}
fn enable(is_enabled: bool) -> impl Filter<Extract = (), Error = warp::Rejection> + 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.
@@ -2379,6 +2394,164 @@ pub fn serve<T: BeaconChainTypes>(
},
);
/*
* beacon/light_client
*/
let beacon_light_client_path = eth_v1
.and(warp::path("beacon"))
.and(warp::path("light_client"))
.and(chain_filter.clone());
// GET beacon/light_client/bootstrap/{block_root}
let get_beacon_light_client_bootstrap = beacon_light_client_path
.clone()
.and(task_spawner_filter.clone())
.and(warp::path("bootstrap"))
.and(warp::path::param::<Hash256>().or_else(|_| async {
Err(warp_utils::reject::custom_bad_request(
"Invalid block root value".to_string(),
))
}))
.and(warp::path::end())
.and(warp::header::optional::<api_types::Accept>("accept"))
.then(
|chain: Arc<BeaconChain<T>>,
task_spawner: TaskSpawner<T::EthSpec>,
block_root: Hash256,
accept_header: Option<api_types::Accept>| {
task_spawner.blocking_response_task(Priority::P1, move || {
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:?}"
)));
}
};
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(&ForkVersionedResponse {
version: Some(fork_name),
data: 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()
.and(task_spawner_filter.clone())
.and(warp::path("optimistic_update"))
.and(warp::path::end())
.and(warp::header::optional::<api_types::Accept>("accept"))
.then(
|chain: Arc<BeaconChain<T>>,
task_spawner: TaskSpawner<T::EthSpec>,
accept_header: Option<api_types::Accept>| {
task_spawner.blocking_response_task(Priority::P1, move || {
let update = chain
.latest_seen_optimistic_update
.lock()
.clone()
.ok_or_else(|| {
warp_utils::reject::custom_not_found(
"No LightClientOptimisticUpdate is available".to_string(),
)
})?;
let fork_name = chain
.spec
.fork_name_at_slot::<T::EthSpec>(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(&ForkVersionedResponse {
version: Some(fork_name),
data: 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(task_spawner_filter.clone())
.and(warp::path("finality_update"))
.and(warp::path::end())
.and(warp::header::optional::<api_types::Accept>("accept"))
.then(
|chain: Arc<BeaconChain<T>>,
task_spawner: TaskSpawner<T::EthSpec>,
accept_header: Option<api_types::Accept>| {
task_spawner.blocking_response_task(Priority::P1, move || {
let update = chain
.latest_seen_finality_update
.lock()
.clone()
.ok_or_else(|| {
warp_utils::reject::custom_not_found(
"No LightClientFinalityUpdate is available".to_string(),
)
})?;
let fork_name = chain
.spec
.fork_name_at_slot::<T::EthSpec>(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(&ForkVersionedResponse {
version: Some(fork_name),
data: update,
})
.into_response()),
}
.map(|resp| add_consensus_version_header(resp, fork_name))
})
},
);
/*
* beacon/rewards
*/
@@ -4339,6 +4512,12 @@ pub fn serve<T: BeaconChainTypes>(
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()
}
@@ -4492,6 +4671,18 @@ pub fn serve<T: BeaconChainTypes>(
.uor(get_lighthouse_database_info)
.uor(get_lighthouse_block_rewards)
.uor(get_lighthouse_attestation_performance)
.uor(
enable(ctx.config.enable_light_client_server)
.and(get_beacon_light_client_optimistic_update),
)
.uor(
enable(ctx.config.enable_light_client_server)
.and(get_beacon_light_client_finality_update),
)
.uor(
enable(ctx.config.enable_light_client_server)
.and(get_beacon_light_client_bootstrap),
)
.uor(get_lighthouse_block_packing_efficiency)
.uor(get_lighthouse_merge_readiness)
.uor(get_events)