Skip to content
Closed
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
2 changes: 1 addition & 1 deletion beacon_node/http_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2571,7 +2571,7 @@ pub async fn serve<T: BeaconChainTypes>(
task_spawner_filter.clone(),
);

// GET validator/execution_payload_envelopes/{slot}/{builder_index}
// GET validator/execution_payload_envelopes/{slot}/{beacon_block_root}
let get_validator_execution_payload_envelopes = get_validator_execution_payload_envelopes(
eth_v1.clone(),
chain_filter.clone(),
Expand Down
17 changes: 12 additions & 5 deletions beacon_node/http_api/src/validator/execution_payload_envelopes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ use eth2::types::Accept;
use ssz::Encode;
use std::sync::Arc;
use tracing::debug;
use types::Slot;
use types::{Hash256, Slot};
use warp::{Filter, Rejection, http::response::Builder, reply::Reply};

// GET validator/execution_payload_envelopes/{slot}
// GET validator/execution_payload_envelopes/{slot}/{beacon_block_root}
pub fn get_validator_execution_payload_envelopes<T: BeaconChainTypes>(
eth_v1: EthV1Filter,
chain_filter: ChainFilter<T>,
Expand All @@ -26,31 +26,38 @@ pub fn get_validator_execution_payload_envelopes<T: BeaconChainTypes>(
"Invalid slot".to_string(),
))
}))
.and(warp::path::param::<Hash256>().or_else(|_| async {
Err(warp_utils::reject::custom_bad_request(
"Invalid beacon_block_root".to_string(),
))
}))
.and(warp::path::end())
.and(warp::header::optional::<Accept>("accept"))
.and(not_while_syncing_filter)
.and(task_spawner_filter)
.and(chain_filter)
.then(
|slot: Slot,
beacon_block_root: Hash256,
accept_header: Option<Accept>,
not_synced_filter: Result<(), Rejection>,
task_spawner: TaskSpawner<T::EthSpec>,
chain: Arc<BeaconChain<T>>| {
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
debug!(?slot, "Execution payload envelope request from HTTP API");
debug!(?slot, ?beacon_block_root, "Execution payload envelope request from HTTP API");

not_synced_filter?;

// Get the envelope from the pending cache (local building only)
// Get the envelope from the pending cache (local building only).
let envelope = chain
.pending_payload_envelopes
.read()
.get(slot)
.filter(|envelope| envelope.beacon_block_root == beacon_block_root)
.cloned()
.ok_or_else(|| {
warp_utils::reject::custom_not_found(format!(
"Execution payload envelope not available for slot {slot}"
"Execution payload envelope not available for slot {slot} and root {beacon_block_root:?}"
))
})?;

Expand Down
17 changes: 14 additions & 3 deletions beacon_node/http_api/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4441,13 +4441,24 @@ impl ApiTester {

let envelope = self
.client
.get_validator_execution_payload_envelopes::<E>(slot)
.get_validator_execution_payload_envelopes::<E>(slot, block.tree_hash_root())
.await
.unwrap()
.data;

self.assert_envelope_fields(&envelope, block.tree_hash_root(), slot);

// A request for a root this node did not build must miss (reorg-resistance).
assert!(
self.client
.get_validator_execution_payload_envelopes::<E>(
slot,
Hash256::repeat_byte(0xff)
)
.await
.is_err()
);

let signed_block = block.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
let signed_block_request =
PublishBlockRequest::try_from(Arc::new(signed_block.clone())).unwrap();
Expand Down Expand Up @@ -4503,7 +4514,7 @@ impl ApiTester {

let envelope = self
.client
.get_validator_execution_payload_envelopes_ssz::<E>(slot)
.get_validator_execution_payload_envelopes_ssz::<E>(slot, block.tree_hash_root())
.await
.unwrap();

Expand Down Expand Up @@ -4963,7 +4974,7 @@ impl ApiTester {
// Retrieve and publish the envelope.
let envelope = self
.client
.get_validator_execution_payload_envelopes::<E>(slot)
.get_validator_execution_payload_envelopes::<E>(slot, block_root)
.await
.unwrap()
.data;
Expand Down
12 changes: 8 additions & 4 deletions common/eth2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2761,34 +2761,38 @@ impl BeaconNodeHttpClient {
opt_response.ok_or(Error::StatusCode(StatusCode::NOT_FOUND))
}

/// `GET v1/validator/execution_payload_envelopes/{slot}`
/// `GET v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root}`
pub async fn get_validator_execution_payload_envelopes<E: EthSpec>(
&self,
slot: Slot,
beacon_block_root: Hash256,
) -> Result<ForkVersionedResponse<ExecutionPayloadEnvelope<E>>, Error> {
let mut path = self.eth_path(V1)?;

path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("execution_payload_envelopes")
.push(&slot.to_string());
.push(&slot.to_string())
.push(&beacon_block_root.to_string());

self.get(path).await
}

/// `GET v1/validator/execution_payload_envelopes/{slot}` in SSZ format
/// `GET v1/validator/execution_payload_envelopes/{slot}/{beacon_block_root}` in SSZ format
pub async fn get_validator_execution_payload_envelopes_ssz<E: EthSpec>(
&self,
slot: Slot,
beacon_block_root: Hash256,
) -> Result<ExecutionPayloadEnvelope<E>, Error> {
let mut path = self.eth_path(V1)?;

path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("execution_payload_envelopes")
.push(&slot.to_string());
.push(&slot.to_string())
.push(&beacon_block_root.to_string());

let opt_response = self
.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_validator_block)
Expand Down
20 changes: 15 additions & 5 deletions validator_client/validator_services/src/block_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use std::time::Duration;
use task_executor::TaskExecutor;
use tokio::sync::mpsc;
use tracing::{Instrument, debug, error, info, info_span, instrument, trace, warn};
use types::{BlockType, ChainSpec, EthSpec, Graffiti, Slot};
use types::{BlockType, ChainSpec, EthSpec, Graffiti, Hash256, Slot};
use validator_store::{Error as ValidatorStoreError, SignedBlock, UnsignedBlock, ValidatorStore};

#[derive(Debug)]
Expand Down Expand Up @@ -611,6 +611,11 @@ impl<S: ValidatorStore + 'static, T: SlotClock + 'static> BlockService<S, T> {
));
}

// Capture before `sign_and_publish_block` moves `unsigned_block`.
let produced_block_root = fork_name
.gloas_enabled()
.then(|| unsigned_block.block_root());

self_ref
.sign_and_publish_block(
&proposer_fallback,
Expand All @@ -624,11 +629,12 @@ impl<S: ValidatorStore + 'static, T: SlotClock + 'static> BlockService<S, T> {
// TODO(gloas) we only need to fetch, sign and publish the envelope in the local building case.
// Right now we always default to local building. Once we implement trustless/trusted builder logic
// we should check the bid for index == BUILDER_INDEX_SELF_BUILD
if fork_name.gloas_enabled() {
if let Some(beacon_block_root) = produced_block_root {
self_ref
.fetch_sign_and_publish_payload_envelope(
&proposer_fallback,
slot,
beacon_block_root,
&validator_pubkey,
)
.await?;
Expand All @@ -649,17 +655,21 @@ impl<S: ValidatorStore + 'static, T: SlotClock + 'static> BlockService<S, T> {
&self,
_proposer_fallback: &ProposerFallback<T>,
slot: Slot,
beacon_block_root: Hash256,
validator_pubkey: &PublicKeyBytes,
) -> Result<(), BlockError> {
info!(slot = slot.as_u64(), "Fetching execution payload envelope");
info!(
slot = slot.as_u64(),
%beacon_block_root,
"Fetching execution payload envelope"
);

// Fetch the envelope from the beacon node.
// TODO(gloas): Use proposer_fallback once multi-BN is supported.
let envelope = self
.beacon_nodes
.first_success(|beacon_node| async move {
beacon_node
.get_validator_execution_payload_envelopes_ssz::<S::E>(slot)
.get_validator_execution_payload_envelopes_ssz::<S::E>(slot, beacon_block_root)
.await
.map_err(|e| {
BlockError::Recoverable(format!(
Expand Down
14 changes: 14 additions & 0 deletions validator_client/validator_store/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,20 @@ pub enum UnsignedBlock<E: EthSpec> {
Blinded(BlindedBeaconBlock<E>),
}

impl<E: EthSpec> UnsignedBlock<E> {
/// The canonical root of this beacon block (identical for the full and blinded forms).
///
/// This is the root of the block *this node produced*, which the local beacon node keys its
/// pending payload-envelope cache by. It is not necessarily the block ultimately signed or
/// published (e.g. under distributed validators).
pub fn block_root(&self) -> Hash256 {
match self {
UnsignedBlock::Full(block) => block.block().canonical_root(),
UnsignedBlock::Blinded(block) => block.canonical_root(),
}
}
}

#[derive(Clone, Debug, PartialEq)]
pub enum SignedBlock<E: EthSpec> {
Full(PublishBlockRequest<E>),
Expand Down
Loading