mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-30 11:13:34 +00:00
Gloas ptc duties beacon node response (#8415)
Co-Authored-By: shane-moore <skm1790@gmail.com> Co-Authored-By: Eitan Seri-Levi <eserilev@ucsc.edu> Co-Authored-By: Eitan Seri-Levi <eserilev@gmail.com> Co-Authored-By: dapplion <35266934+dapplion@users.noreply.github.com>
This commit is contained in:
@@ -19,6 +19,7 @@ mod metrics;
|
||||
mod peer;
|
||||
mod produce_block;
|
||||
mod proposer_duties;
|
||||
mod ptc_duties;
|
||||
mod publish_attestations;
|
||||
mod publish_blocks;
|
||||
mod standard_block_rewards;
|
||||
@@ -2560,6 +2561,14 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
task_spawner_filter.clone(),
|
||||
);
|
||||
|
||||
// POST validator/duties/ptc/{epoch}
|
||||
let post_validator_duties_ptc = post_validator_duties_ptc(
|
||||
eth_v1.clone(),
|
||||
chain_filter.clone(),
|
||||
not_while_syncing_filter.clone(),
|
||||
task_spawner_filter.clone(),
|
||||
);
|
||||
|
||||
// POST validator/duties/sync/{epoch}
|
||||
let post_validator_duties_sync = post_validator_duties_sync(
|
||||
eth_v1.clone(),
|
||||
@@ -3410,6 +3419,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.uor(post_beacon_rewards_attestations)
|
||||
.uor(post_beacon_rewards_sync_committee)
|
||||
.uor(post_validator_duties_attester)
|
||||
.uor(post_validator_duties_ptc)
|
||||
.uor(post_validator_duties_sync)
|
||||
.uor(post_validator_aggregate_and_proofs)
|
||||
.uor(post_validator_contribution_and_proofs)
|
||||
|
||||
182
beacon_node/http_api/src/ptc_duties.rs
Normal file
182
beacon_node/http_api/src/ptc_duties.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
//! Contains the handler for the `POST validator/duties/ptc/{epoch}` endpoint.
|
||||
|
||||
use crate::state_id::StateId;
|
||||
use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes};
|
||||
use eth2::types::{self as api_types, PtcDuty};
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::state_advance::partial_state_advance;
|
||||
use types::{BeaconState, ChainSpec, Epoch, EthSpec, Hash256};
|
||||
|
||||
type ApiDuties = api_types::DutiesResponse<Vec<PtcDuty>>;
|
||||
|
||||
pub fn ptc_duties<T: BeaconChainTypes>(
|
||||
request_epoch: Epoch,
|
||||
request_indices: &[u64],
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<ApiDuties, warp::reject::Rejection> {
|
||||
let current_epoch = chain
|
||||
.slot_clock
|
||||
.now_or_genesis()
|
||||
.map(|slot| slot.epoch(T::EthSpec::slots_per_epoch()))
|
||||
.ok_or(BeaconChainError::UnableToReadSlot)
|
||||
.map_err(warp_utils::reject::unhandled_error)?;
|
||||
|
||||
let tolerant_current_epoch = if chain.slot_clock.is_prior_to_genesis().unwrap_or(true) {
|
||||
current_epoch
|
||||
} else {
|
||||
chain
|
||||
.slot_clock
|
||||
.now_with_future_tolerance(chain.spec.maximum_gossip_clock_disparity())
|
||||
.ok_or_else(|| {
|
||||
warp_utils::reject::custom_server_error("unable to read slot clock".into())
|
||||
})?
|
||||
.epoch(T::EthSpec::slots_per_epoch())
|
||||
};
|
||||
|
||||
let is_within_clock_tolerance = request_epoch == current_epoch
|
||||
|| request_epoch == current_epoch + 1
|
||||
|| request_epoch == tolerant_current_epoch + 1;
|
||||
|
||||
if is_within_clock_tolerance {
|
||||
let head_epoch = chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.snapshot
|
||||
.beacon_state
|
||||
.current_epoch();
|
||||
|
||||
let head_can_serve_request = request_epoch == head_epoch || request_epoch == head_epoch + 1;
|
||||
|
||||
if head_can_serve_request {
|
||||
compute_ptc_duties_from_cached_head(request_epoch, request_indices, chain)
|
||||
} else {
|
||||
compute_ptc_duties_from_state(request_epoch, request_indices, chain)
|
||||
}
|
||||
} else if request_epoch > current_epoch + 1 {
|
||||
Err(warp_utils::reject::custom_bad_request(format!(
|
||||
"request epoch {} is more than one epoch past the current epoch {}",
|
||||
request_epoch, current_epoch
|
||||
)))
|
||||
} else {
|
||||
compute_ptc_duties_from_state(request_epoch, request_indices, chain)
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_ptc_duties_from_cached_head<T: BeaconChainTypes>(
|
||||
request_epoch: Epoch,
|
||||
request_indices: &[u64],
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<ApiDuties, warp::reject::Rejection> {
|
||||
let (cached_head, execution_status) = chain
|
||||
.canonical_head
|
||||
.head_and_execution_status()
|
||||
.map_err(warp_utils::reject::unhandled_error)?;
|
||||
let state = &cached_head.snapshot.beacon_state;
|
||||
let head_block_root = cached_head.head_block_root();
|
||||
|
||||
let (duties, dependent_root) = chain
|
||||
.compute_ptc_duties(state, request_epoch, request_indices, head_block_root)
|
||||
.map_err(warp_utils::reject::unhandled_error)?;
|
||||
|
||||
convert_to_api_response(
|
||||
duties,
|
||||
dependent_root,
|
||||
execution_status.is_optimistic_or_invalid(),
|
||||
)
|
||||
}
|
||||
|
||||
fn compute_ptc_duties_from_state<T: BeaconChainTypes>(
|
||||
request_epoch: Epoch,
|
||||
request_indices: &[u64],
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<ApiDuties, warp::reject::Rejection> {
|
||||
let state_opt = {
|
||||
let (cached_head, execution_status) = chain
|
||||
.canonical_head
|
||||
.head_and_execution_status()
|
||||
.map_err(warp_utils::reject::unhandled_error)?;
|
||||
let head = &cached_head.snapshot;
|
||||
|
||||
if head.beacon_state.current_epoch() <= request_epoch {
|
||||
Some((
|
||||
head.beacon_state_root(),
|
||||
head.beacon_state.clone(),
|
||||
execution_status.is_optimistic_or_invalid(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
let (state, execution_optimistic) =
|
||||
if let Some((state_root, mut state, execution_optimistic)) = state_opt {
|
||||
ensure_state_knows_ptc_duties_for_epoch(
|
||||
&mut state,
|
||||
state_root,
|
||||
request_epoch,
|
||||
&chain.spec,
|
||||
)?;
|
||||
(state, execution_optimistic)
|
||||
} else {
|
||||
let (state, execution_optimistic, _finalized) =
|
||||
StateId::from_slot(request_epoch.start_slot(T::EthSpec::slots_per_epoch()))
|
||||
.state(chain)?;
|
||||
(state, execution_optimistic)
|
||||
};
|
||||
|
||||
if !(state.current_epoch() == request_epoch || state.current_epoch() + 1 == request_epoch) {
|
||||
return Err(warp_utils::reject::custom_server_error(format!(
|
||||
"state epoch {} not suitable for request epoch {}",
|
||||
state.current_epoch(),
|
||||
request_epoch
|
||||
)));
|
||||
}
|
||||
|
||||
let (duties, dependent_root) = chain
|
||||
.compute_ptc_duties(
|
||||
&state,
|
||||
request_epoch,
|
||||
request_indices,
|
||||
chain.genesis_block_root,
|
||||
)
|
||||
.map_err(warp_utils::reject::unhandled_error)?;
|
||||
|
||||
convert_to_api_response(duties, dependent_root, execution_optimistic)
|
||||
}
|
||||
|
||||
fn ensure_state_knows_ptc_duties_for_epoch<E: EthSpec>(
|
||||
state: &mut BeaconState<E>,
|
||||
state_root: Hash256,
|
||||
target_epoch: Epoch,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<(), warp::reject::Rejection> {
|
||||
if state.current_epoch() > target_epoch {
|
||||
return Err(warp_utils::reject::custom_server_error(format!(
|
||||
"state epoch {} is later than target epoch {}",
|
||||
state.current_epoch(),
|
||||
target_epoch
|
||||
)));
|
||||
} else if state.current_epoch() + 1 < target_epoch {
|
||||
let target_slot = target_epoch
|
||||
.saturating_sub(1_u64)
|
||||
.start_slot(E::slots_per_epoch());
|
||||
|
||||
partial_state_advance(state, Some(state_root), target_slot, spec)
|
||||
.map_err(BeaconChainError::from)
|
||||
.map_err(warp_utils::reject::unhandled_error)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn convert_to_api_response(
|
||||
duties: Vec<Option<PtcDuty>>,
|
||||
dependent_root: Hash256,
|
||||
execution_optimistic: bool,
|
||||
) -> Result<ApiDuties, warp::reject::Rejection> {
|
||||
Ok(api_types::DutiesResponse {
|
||||
dependent_root,
|
||||
execution_optimistic: Some(execution_optimistic),
|
||||
data: duties.into_iter().flatten().collect(),
|
||||
})
|
||||
}
|
||||
@@ -7,7 +7,7 @@ use crate::utils::{
|
||||
ResponseFilter, TaskSpawnerFilter, ValidatorSubscriptionTxFilter, publish_network_message,
|
||||
};
|
||||
use crate::version::{V1, V2, V3, unsupported_version_rejection};
|
||||
use crate::{StateId, attester_duties, proposer_duties, sync_committees};
|
||||
use crate::{StateId, attester_duties, proposer_duties, ptc_duties, sync_committees};
|
||||
use beacon_chain::attestation_verification::VerifiedAttestation;
|
||||
use beacon_chain::{AttestationError, BeaconChain, BeaconChainError, BeaconChainTypes};
|
||||
use bls::PublicKeyBytes;
|
||||
@@ -168,6 +168,42 @@ pub fn post_validator_duties_attester<T: BeaconChainTypes>(
|
||||
.boxed()
|
||||
}
|
||||
|
||||
// POST validator/duties/ptc/{epoch}
|
||||
pub fn post_validator_duties_ptc<T: BeaconChainTypes>(
|
||||
eth_v1: EthV1Filter,
|
||||
chain_filter: ChainFilter<T>,
|
||||
not_while_syncing_filter: NotWhileSyncingFilter,
|
||||
task_spawner_filter: TaskSpawnerFilter<T>,
|
||||
) -> ResponseFilter {
|
||||
eth_v1
|
||||
.and(warp::path("validator"))
|
||||
.and(warp::path("duties"))
|
||||
.and(warp::path("ptc"))
|
||||
.and(warp::path::param::<Epoch>().or_else(|_| async {
|
||||
Err(warp_utils::reject::custom_bad_request(
|
||||
"Invalid epoch".to_string(),
|
||||
))
|
||||
}))
|
||||
.and(warp::path::end())
|
||||
.and(not_while_syncing_filter.clone())
|
||||
.and(warp_utils::json::json())
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.then(
|
||||
|epoch: Epoch,
|
||||
not_synced_filter: Result<(), Rejection>,
|
||||
indices: ValidatorIndexData,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>| {
|
||||
task_spawner.blocking_json_task(Priority::P0, move || {
|
||||
not_synced_filter?;
|
||||
ptc_duties::ptc_duties(epoch, &indices.0, &chain)
|
||||
})
|
||||
},
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
|
||||
// GET validator/aggregate_attestation?attestation_data_root,slot
|
||||
pub fn get_validator_aggregate_attestation<T: BeaconChainTypes>(
|
||||
any_version: AnyVersionFilter,
|
||||
|
||||
Reference in New Issue
Block a user