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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2225,6 +2225,39 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
})
}

/// Produce the inclusion list transactions for `request_slot`.
///
/// The transactions are requested from the execution layer via
/// `getInclusionListV1`, built on top of the current head.
pub async fn produce_inclusion_list(
&self,
request_slot: Slot,
) -> Result<ProgressiveTransactions, Error> {
// Inclusion lists are only produced for the current slot.
let current_slot = self.slot()?;
if request_slot != current_slot {
return Err(Error::InvalidSlot(request_slot));
}

let execution_layer = self
.execution_layer
.as_ref()
.ok_or(Error::ExecutionLayerMissing)?;

let fcu_params = self
.canonical_head
.cached_head()
.forkchoice_update_parameters();
let head_hash = fcu_params
.head_hash
.ok_or(Error::ExecutionHashMissingFromHead(fcu_params.head_root))?;

execution_layer
.get_inclusion_list_v1(head_hash)
.await
.map_err(|e| Error::ExecutionLayerGetInclusionListFailed(Box::new(e)))
}

/// Performs the same validation as `Self::verify_unaggregated_attestation_for_gossip`, but for
/// multiple attestations using batch BLS verification. Batch verification can provide
/// significant CPU-time savings compared to individual verification.
Expand Down
2 changes: 2 additions & 0 deletions beacon_node/beacon_chain/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ pub enum BeaconChainError {
BlockVariantLacksExecutionPayload(Hash256),
ExecutionLayerErrorPayloadReconstruction(ExecutionBlockHash, Box<execution_layer::Error>),
ExecutionLayerGetBlockByNumberFailed(Box<execution_layer::Error>),
ExecutionLayerGetInclusionListFailed(Box<execution_layer::Error>),
ExecutionHashMissingFromHead(Hash256),
BlockHashMissingFromExecutionLayer(ExecutionBlockHash),
InconsistentPayloadReconstructed {
slot: Slot,
Expand Down
9 changes: 9 additions & 0 deletions beacon_node/http_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2601,6 +2601,14 @@ pub async fn serve<T: BeaconChainTypes>(
task_spawner_filter.clone(),
);

// GET validator/inclusion_list?slot
let get_validator_inclusion_list = get_validator_inclusion_list(
eth_v1.clone(),
chain_filter.clone(),
not_while_syncing_filter.clone(),
task_spawner_filter.clone(),
);

// GET validator/aggregate_attestation?attestation_data_root,slot
let get_validator_aggregate_attestation = get_validator_aggregate_attestation(
any_version.clone(),
Expand Down Expand Up @@ -3426,6 +3434,7 @@ pub async fn serve<T: BeaconChainTypes>(
.uor(get_validator_execution_payload_envelopes)
.uor(get_validator_attestation_data)
.uor(get_validator_payload_attestation_data)
.uor(get_validator_inclusion_list)
.uor(get_validator_aggregate_attestation)
.uor(get_validator_sync_committee_contribution)
.uor(get_lighthouse_health)
Expand Down
62 changes: 60 additions & 2 deletions beacon_node/http_api/src/validator/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ use context_deserialize::ContextDeserialize;
use eth2::CONSENSUS_VERSION_HEADER;
use eth2::types::{
Accept, BeaconCommitteeSubscription, EndpointVersion, Failure, GenericResponse,
StandardLivenessResponseData, StateId as CoreStateId, ValidatorAggregateAttestationQuery,
ValidatorAttestationDataQuery, ValidatorBlocksQuery, ValidatorIndexData, ValidatorStatus,
InclusionListTransactions, StandardLivenessResponseData, StateId as CoreStateId,
ValidatorAggregateAttestationQuery, ValidatorAttestationDataQuery, ValidatorBlocksQuery,
ValidatorInclusionListQuery, ValidatorIndexData, ValidatorStatus,
};
use lighthouse_network::PubsubMessage;
use network::{NetworkMessage, ValidatorSubscriptionMessage};
Expand Down Expand Up @@ -412,6 +413,63 @@ pub fn get_validator_payload_attestation_data<T: BeaconChainTypes>(
.boxed()
}

// GET validator/inclusion_list?slot
pub fn get_validator_inclusion_list<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("inclusion_list"))
.and(warp::path::end())
.and(warp::query::<ValidatorInclusionListQuery>())
.and(not_while_syncing_filter)
.and(task_spawner_filter)
.and(chain_filter)
.then(
|query: ValidatorInclusionListQuery,
not_synced_filter: Result<(), Rejection>,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| {
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
not_synced_filter?;

let slot = query.slot;
let fork_name = chain.spec.fork_name_at_slot::<T::EthSpec>(slot);

// Inclusion lists are only valid for Heze and later forks.
if !fork_name.heze_enabled() {
return Err(warp_utils::reject::custom_bad_request(format!(
"Inclusion lists are not supported for fork: {fork_name}"
)));
}

let transactions =
chain
.produce_inclusion_list(slot)
.await
.map_err(|e| match e {
BeaconChainError::InvalidSlot(_) => {
warp_utils::reject::custom_bad_request(format!(
"Unable to produce inclusion list: {e:?}"
))
}
_ => warp_utils::reject::custom_server_error(format!(
"Unable to produce inclusion list: {e:?}"
)),
})?;

let response =
GenericResponse::from(InclusionListTransactions { transactions });
Ok(warp::reply::json(&response).into_response())
})
},
)
.boxed()
}

// GET validator/blinded_blocks/{slot}
pub fn get_validator_blinded_blocks<T: BeaconChainTypes>(
eth_v1: EthV1Filter,
Expand Down
29 changes: 29 additions & 0 deletions beacon_node/http_api/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6007,6 +6007,18 @@ impl ApiTester {
self
}

pub async fn test_get_validator_inclusion_list_pre_heze(self) -> Self {
let slot = self.chain.slot().unwrap();

// The endpoint should return a 400 error for pre-Heze forks.
match self.client.get_validator_inclusion_list(slot).await {
Ok(result) => panic!("query for a pre-Heze slot should fail, got: {result:?}"),
Err(e) => assert_eq!(e.status().unwrap(), 400),
}

self
}

pub async fn test_get_validator_payload_attestation_data_no_block(self) -> Self {
// Advance the slot clock without producing a block
self.harness.advance_slot();
Expand Down Expand Up @@ -10065,6 +10077,23 @@ async fn get_validator_payload_attestation_data_pre_gloas() {
.await;
}

// TODO(heze): add IL fetching tests for:
// - happy-path
// - bad-slot
// - EL call failure
//
// The above tests should be added once the harness supports building a Heze chain.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_validator_inclusion_list_pre_heze() {
if fork_name_from_env().is_some_and(|f| f.heze_enabled()) {
return;
}
ApiTester::new()
.await
.test_get_validator_inclusion_list_pre_heze()
.await;
}

#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn get_validator_payload_attestation_data_no_block() {
if !fork_name_from_env().is_some_and(|f| f.gloas_enabled()) {
Expand Down
18 changes: 18 additions & 0 deletions common/eth2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3716,6 +3716,24 @@ impl BeaconNodeHttpClient {
.transpose()
}

/// `GET validator/inclusion_list?slot`
pub async fn get_validator_inclusion_list(
&self,
slot: Slot,
) -> Result<GenericResponse<InclusionListTransactions>, Error> {
let mut path = self.eth_path(V1)?;

path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("inclusion_list");

path.query_pairs_mut()
.append_pair("slot", &slot.to_string());

self.get(path).await
}

/// `GET v1/validator/aggregate_attestation?slot,attestation_data_root`
pub async fn get_validator_aggregate_attestation_v1<E: EthSpec>(
&self,
Expand Down
12 changes: 12 additions & 0 deletions common/eth2/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,18 @@ impl TryFrom<Option<String>> for SkipRandaoVerification {
}
}

#[derive(Clone, Serialize, Deserialize)]
pub struct ValidatorInclusionListQuery {
pub slot: Slot,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct InclusionListTransactions {
#[serde(with = "ssz_types::serde_utils::prog_list_of_hex_prog_var_list")]
pub transactions: ProgressiveTransactions,
}

#[derive(Clone, Serialize, Deserialize)]
pub struct ValidatorAttestationDataQuery {
pub slot: Slot,
Expand Down
Loading