mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-09 19:51:47 +00:00
Remove builder redundancy (#3294)
## Issue Addressed This PR is a subset of the changes in #3134. Unstable will still not function correctly with the new builder spec once this is merged, #3134 should be used on testnets ## Proposed Changes - Removes redundancy in "builders" (servers implementing the builder spec) - Renames `payload-builder` flag to `builder` - Moves from old builder RPC API to new HTTP API, but does not implement the validator registration API (implemented in https://github.com/sigp/lighthouse/pull/3194) Co-authored-by: sean <seananderson33@gmail.com> Co-authored-by: realbigsean <sean@sigmaprime.io>
This commit is contained in:
@@ -4,9 +4,8 @@
|
||||
//! This crate only provides useful functionality for "The Merge", it does not provide any of the
|
||||
//! deposit-contract functionality that the `beacon_node/eth1` crate already provides.
|
||||
|
||||
use crate::engine_api::Builder;
|
||||
use crate::engines::Builders;
|
||||
use auth::{strip_prefix, Auth, JwtKey};
|
||||
use builder_client::BuilderHttpClient;
|
||||
use engine_api::Error as ApiError;
|
||||
pub use engine_api::*;
|
||||
pub use engine_api::{http, http::deposit_methods, http::HttpJsonRpc};
|
||||
@@ -20,7 +19,6 @@ use serde::{Deserialize, Serialize};
|
||||
use slog::{crit, debug, error, info, trace, warn, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
use std::future::Future;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
@@ -33,7 +31,7 @@ use tokio::{
|
||||
};
|
||||
use types::{
|
||||
BlindedPayload, BlockType, ChainSpec, Epoch, ExecPayload, ExecutionBlockHash,
|
||||
ProposerPreparationData, SignedBeaconBlock, Slot,
|
||||
ProposerPreparationData, PublicKeyBytes, SignedBeaconBlock, Slot,
|
||||
};
|
||||
|
||||
mod engine_api;
|
||||
@@ -69,6 +67,7 @@ pub enum Error {
|
||||
NoEngines,
|
||||
NoPayloadBuilder,
|
||||
ApiError(ApiError),
|
||||
Builder(builder_client::Error),
|
||||
EngineErrors(Vec<EngineError>),
|
||||
NotSynced,
|
||||
ShuttingDown,
|
||||
@@ -102,15 +101,16 @@ pub struct Proposer {
|
||||
payload_attributes: PayloadAttributes,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
struct Inner<E: EthSpec> {
|
||||
engines: Engines,
|
||||
builders: Builders,
|
||||
builder: Option<BuilderHttpClient>,
|
||||
execution_engine_forkchoice_lock: Mutex<()>,
|
||||
suggested_fee_recipient: Option<Address>,
|
||||
proposer_preparation_data: Mutex<HashMap<u64, ProposerPreparationDataEntry>>,
|
||||
execution_blocks: Mutex<LruCache<ExecutionBlockHash, ExecutionBlock>>,
|
||||
proposers: RwLock<HashMap<ProposerKey, Proposer>>,
|
||||
executor: TaskExecutor,
|
||||
phantom: std::marker::PhantomData<E>,
|
||||
log: Logger,
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ pub struct Config {
|
||||
/// Endpoint urls for EL nodes that are running the engine api.
|
||||
pub execution_endpoints: Vec<SensitiveUrl>,
|
||||
/// Endpoint urls for services providing the builder api.
|
||||
pub builder_endpoints: Vec<SensitiveUrl>,
|
||||
pub builder_url: Option<SensitiveUrl>,
|
||||
/// JWT secrets for the above endpoints running the engine api.
|
||||
pub secret_files: Vec<PathBuf>,
|
||||
/// The default fee recipient to use on the beacon node if none if provided from
|
||||
@@ -143,16 +143,16 @@ pub struct Config {
|
||||
///
|
||||
/// The fallback nodes have an ordering. The first supplied will be the first contacted, and so on.
|
||||
#[derive(Clone)]
|
||||
pub struct ExecutionLayer {
|
||||
inner: Arc<Inner>,
|
||||
pub struct ExecutionLayer<T: EthSpec> {
|
||||
inner: Arc<Inner<T>>,
|
||||
}
|
||||
|
||||
impl ExecutionLayer {
|
||||
impl<T: EthSpec> ExecutionLayer<T> {
|
||||
/// Instantiate `Self` with Execution engines specified using `Config`, all using the JSON-RPC via HTTP.
|
||||
pub fn from_config(config: Config, executor: TaskExecutor, log: Logger) -> Result<Self, Error> {
|
||||
let Config {
|
||||
execution_endpoints: urls,
|
||||
builder_endpoints: builder_urls,
|
||||
builder_url,
|
||||
secret_files,
|
||||
suggested_fee_recipient,
|
||||
jwt_id,
|
||||
@@ -208,14 +208,9 @@ impl ExecutionLayer {
|
||||
Engine::<EngineApi>::new(id, api)
|
||||
};
|
||||
|
||||
let builders: Vec<Engine<BuilderApi>> = builder_urls
|
||||
.into_iter()
|
||||
.map(|url| {
|
||||
let id = url.to_string();
|
||||
let api = HttpJsonRpc::<BuilderApi>::new(url)?;
|
||||
Ok(Engine::<BuilderApi>::new(id, api))
|
||||
})
|
||||
.collect::<Result<_, ApiError>>()?;
|
||||
let builder = builder_url
|
||||
.map(|url| BuilderHttpClient::new(url).map_err(Error::Builder))
|
||||
.transpose()?;
|
||||
|
||||
let inner = Inner {
|
||||
engines: Engines {
|
||||
@@ -223,16 +218,14 @@ impl ExecutionLayer {
|
||||
latest_forkchoice_state: <_>::default(),
|
||||
log: log.clone(),
|
||||
},
|
||||
builders: Builders {
|
||||
builders,
|
||||
log: log.clone(),
|
||||
},
|
||||
builder,
|
||||
execution_engine_forkchoice_lock: <_>::default(),
|
||||
suggested_fee_recipient,
|
||||
proposer_preparation_data: Mutex::new(HashMap::new()),
|
||||
proposers: RwLock::new(HashMap::new()),
|
||||
execution_blocks: Mutex::new(LruCache::new(EXECUTION_BLOCKS_LRU_CACHE_SIZE)),
|
||||
executor,
|
||||
phantom: std::marker::PhantomData,
|
||||
log,
|
||||
};
|
||||
|
||||
@@ -242,13 +235,13 @@ impl ExecutionLayer {
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecutionLayer {
|
||||
impl<T: EthSpec> ExecutionLayer<T> {
|
||||
fn engines(&self) -> &Engines {
|
||||
&self.inner.engines
|
||||
}
|
||||
|
||||
fn builders(&self) -> &Builders {
|
||||
&self.inner.builders
|
||||
pub fn builder(&self) -> &Option<BuilderHttpClient> {
|
||||
&self.inner.builder
|
||||
}
|
||||
|
||||
pub fn executor(&self) -> &TaskExecutor {
|
||||
@@ -282,9 +275,9 @@ impl ExecutionLayer {
|
||||
}
|
||||
|
||||
/// Convenience function to allow calling async functions in a non-async context.
|
||||
pub fn block_on<'a, T, U, V>(&'a self, generate_future: T) -> Result<V, Error>
|
||||
pub fn block_on<'a, F, U, V>(&'a self, generate_future: F) -> Result<V, Error>
|
||||
where
|
||||
T: Fn(&'a Self) -> U,
|
||||
F: Fn(&'a Self) -> U,
|
||||
U: Future<Output = Result<V, Error>>,
|
||||
{
|
||||
let runtime = self.executor().handle().ok_or(Error::ShuttingDown)?;
|
||||
@@ -296,9 +289,9 @@ impl ExecutionLayer {
|
||||
///
|
||||
/// The function is "generic" since it does not enforce a particular return type on
|
||||
/// `generate_future`.
|
||||
pub fn block_on_generic<'a, T, U, V>(&'a self, generate_future: T) -> Result<V, Error>
|
||||
pub fn block_on_generic<'a, F, U, V>(&'a self, generate_future: F) -> Result<V, Error>
|
||||
where
|
||||
T: Fn(&'a Self) -> U,
|
||||
F: Fn(&'a Self) -> U,
|
||||
U: Future<Output = V>,
|
||||
{
|
||||
let runtime = self.executor().handle().ok_or(Error::ShuttingDown)?;
|
||||
@@ -307,9 +300,9 @@ impl ExecutionLayer {
|
||||
}
|
||||
|
||||
/// Convenience function to allow spawning a task without waiting for the result.
|
||||
pub fn spawn<T, U>(&self, generate_future: T, name: &'static str)
|
||||
pub fn spawn<F, U>(&self, generate_future: F, name: &'static str)
|
||||
where
|
||||
T: FnOnce(Self) -> U,
|
||||
F: FnOnce(Self) -> U,
|
||||
U: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
self.executor().spawn(generate_future(self.clone()), name);
|
||||
@@ -317,12 +310,12 @@ impl ExecutionLayer {
|
||||
|
||||
/// Spawns a routine which attempts to keep the execution engines online.
|
||||
pub fn spawn_watchdog_routine<S: SlotClock + 'static>(&self, slot_clock: S) {
|
||||
let watchdog = |el: ExecutionLayer| async move {
|
||||
let watchdog = |el: ExecutionLayer<T>| async move {
|
||||
// Run one task immediately.
|
||||
el.watchdog_task().await;
|
||||
|
||||
let recurring_task =
|
||||
|el: ExecutionLayer, now: Instant, duration_to_next_slot: Duration| async move {
|
||||
|el: ExecutionLayer<T>, now: Instant, duration_to_next_slot: Duration| async move {
|
||||
// We run the task three times per slot.
|
||||
//
|
||||
// The interval between each task is 1/3rd of the slot duration. This matches nicely
|
||||
@@ -377,11 +370,8 @@ impl ExecutionLayer {
|
||||
}
|
||||
|
||||
/// Spawns a routine which cleans the cached proposer data periodically.
|
||||
pub fn spawn_clean_proposer_caches_routine<S: SlotClock + 'static, T: EthSpec>(
|
||||
&self,
|
||||
slot_clock: S,
|
||||
) {
|
||||
let preparation_cleaner = |el: ExecutionLayer| async move {
|
||||
pub fn spawn_clean_proposer_caches_routine<S: SlotClock + 'static>(&self, slot_clock: S) {
|
||||
let preparation_cleaner = |el: ExecutionLayer<T>| async move {
|
||||
// Start the loop to periodically clean proposer preparation cache.
|
||||
loop {
|
||||
if let Some(duration_to_next_epoch) =
|
||||
@@ -395,7 +385,7 @@ impl ExecutionLayer {
|
||||
.map(|slot| slot.epoch(T::slots_per_epoch()))
|
||||
{
|
||||
Some(current_epoch) => el
|
||||
.clean_proposer_caches::<T>(current_epoch)
|
||||
.clean_proposer_caches(current_epoch)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
error!(
|
||||
@@ -420,7 +410,7 @@ impl ExecutionLayer {
|
||||
|
||||
/// Spawns a routine that polls the `exchange_transition_configuration` endpoint.
|
||||
pub fn spawn_transition_configuration_poll(&self, spec: ChainSpec) {
|
||||
let routine = |el: ExecutionLayer| async move {
|
||||
let routine = |el: ExecutionLayer<T>| async move {
|
||||
loop {
|
||||
if let Err(e) = el.exchange_transition_configuration(&spec).await {
|
||||
error!(
|
||||
@@ -454,7 +444,7 @@ impl ExecutionLayer {
|
||||
}
|
||||
|
||||
/// Updates the proposer preparation data provided by validators
|
||||
async fn update_proposer_preparation(
|
||||
pub async fn update_proposer_preparation(
|
||||
&self,
|
||||
update_epoch: Epoch,
|
||||
preparation_data: &[ProposerPreparationData],
|
||||
@@ -476,7 +466,7 @@ impl ExecutionLayer {
|
||||
}
|
||||
|
||||
/// Removes expired entries from proposer_preparation_data and proposers caches
|
||||
async fn clean_proposer_caches<T: EthSpec>(&self, current_epoch: Epoch) -> Result<(), Error> {
|
||||
async fn clean_proposer_caches(&self, current_epoch: Epoch) -> Result<(), Error> {
|
||||
let mut proposer_preparation_data = self.proposer_preparation_data().await;
|
||||
|
||||
// Keep all entries that have been updated in the last 2 epochs
|
||||
@@ -561,104 +551,164 @@ impl ExecutionLayer {
|
||||
///
|
||||
/// The result will be returned from the first node that returns successfully. No more nodes
|
||||
/// will be contacted.
|
||||
pub async fn get_payload<T: EthSpec, Payload: ExecPayload<T>>(
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn get_payload<Payload: ExecPayload<T>>(
|
||||
&self,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
timestamp: u64,
|
||||
prev_randao: Hash256,
|
||||
finalized_block_hash: ExecutionBlockHash,
|
||||
proposer_index: u64,
|
||||
pubkey: Option<PublicKeyBytes>,
|
||||
slot: Slot,
|
||||
) -> Result<Payload, Error> {
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::GET_PAYLOAD],
|
||||
);
|
||||
|
||||
let suggested_fee_recipient = self.get_suggested_fee_recipient(proposer_index).await;
|
||||
|
||||
match Payload::block_type() {
|
||||
BlockType::Blinded => {
|
||||
debug!(
|
||||
self.log(),
|
||||
"Issuing builder_getPayloadHeader";
|
||||
"suggested_fee_recipient" => ?suggested_fee_recipient,
|
||||
"prev_randao" => ?prev_randao,
|
||||
"timestamp" => timestamp,
|
||||
"parent_hash" => ?parent_hash,
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::GET_BLINDED_PAYLOAD],
|
||||
);
|
||||
self.builders()
|
||||
.first_success_without_retry(|engine| async move {
|
||||
let payload_id = engine
|
||||
.get_payload_id(
|
||||
parent_hash,
|
||||
timestamp,
|
||||
prev_randao,
|
||||
suggested_fee_recipient,
|
||||
)
|
||||
.await
|
||||
.ok_or(ApiError::MissingPayloadId {
|
||||
parent_hash,
|
||||
timestamp,
|
||||
prev_randao,
|
||||
suggested_fee_recipient,
|
||||
})?;
|
||||
engine
|
||||
.api
|
||||
.get_payload_header_v1::<T>(payload_id)
|
||||
.await?
|
||||
.try_into()
|
||||
.map_err(|_| ApiError::PayloadConversionLogicFlaw)
|
||||
})
|
||||
.await
|
||||
.map_err(Error::EngineErrors)
|
||||
self.get_blinded_payload(
|
||||
parent_hash,
|
||||
timestamp,
|
||||
prev_randao,
|
||||
finalized_block_hash,
|
||||
suggested_fee_recipient,
|
||||
pubkey,
|
||||
slot,
|
||||
)
|
||||
.await
|
||||
}
|
||||
BlockType::Full => {
|
||||
debug!(
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::GET_PAYLOAD],
|
||||
);
|
||||
self.get_full_payload(
|
||||
parent_hash,
|
||||
timestamp,
|
||||
prev_randao,
|
||||
finalized_block_hash,
|
||||
suggested_fee_recipient,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn get_blinded_payload<Payload: ExecPayload<T>>(
|
||||
&self,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
timestamp: u64,
|
||||
prev_randao: Hash256,
|
||||
finalized_block_hash: ExecutionBlockHash,
|
||||
suggested_fee_recipient: Address,
|
||||
pubkey_opt: Option<PublicKeyBytes>,
|
||||
slot: Slot,
|
||||
) -> Result<Payload, Error> {
|
||||
//FIXME(sean) fallback logic included in PR #3134
|
||||
|
||||
// Don't attempt to outsource payload construction until after the merge transition has been
|
||||
// finalized. We want to be conservative with payload construction until then.
|
||||
if let (Some(builder), Some(pubkey)) = (self.builder(), pubkey_opt) {
|
||||
if finalized_block_hash != ExecutionBlockHash::zero() {
|
||||
info!(
|
||||
self.log(),
|
||||
"Issuing engine_getPayload";
|
||||
"suggested_fee_recipient" => ?suggested_fee_recipient,
|
||||
"prev_randao" => ?prev_randao,
|
||||
"timestamp" => timestamp,
|
||||
"Requesting blinded header from connected builder";
|
||||
"slot" => ?slot,
|
||||
"pubkey" => ?pubkey,
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
self.engines()
|
||||
.first_success(|engine| async move {
|
||||
let payload_id = if let Some(id) = engine
|
||||
.get_payload_id(
|
||||
parent_hash,
|
||||
timestamp,
|
||||
prev_randao,
|
||||
suggested_fee_recipient,
|
||||
)
|
||||
.await
|
||||
{
|
||||
// The payload id has been cached for this engine.
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_PRE_PREPARED_PAYLOAD_ID,
|
||||
&[metrics::HIT],
|
||||
);
|
||||
id
|
||||
} else {
|
||||
// The payload id has *not* been cached for this engine. Trigger an artificial
|
||||
// fork choice update to retrieve a payload ID.
|
||||
//
|
||||
// TODO(merge): a better algorithm might try to favour a node that already had a
|
||||
// cached payload id, since a payload that has had more time to produce is
|
||||
// likely to be more profitable.
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_PRE_PREPARED_PAYLOAD_ID,
|
||||
&[metrics::MISS],
|
||||
);
|
||||
let fork_choice_state = ForkChoiceState {
|
||||
head_block_hash: parent_hash,
|
||||
safe_block_hash: parent_hash,
|
||||
finalized_block_hash,
|
||||
};
|
||||
let payload_attributes = PayloadAttributes {
|
||||
timestamp,
|
||||
prev_randao,
|
||||
suggested_fee_recipient,
|
||||
};
|
||||
return builder
|
||||
.get_builder_header::<T, Payload>(slot, parent_hash, &pubkey)
|
||||
.await
|
||||
.map(|d| d.data.message.header)
|
||||
.map_err(Error::Builder);
|
||||
}
|
||||
}
|
||||
self.get_full_payload::<Payload>(
|
||||
parent_hash,
|
||||
timestamp,
|
||||
prev_randao,
|
||||
finalized_block_hash,
|
||||
suggested_fee_recipient,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// Get a full payload without caching its result in the execution layer's payload cache.
|
||||
async fn get_full_payload<Payload: ExecPayload<T>>(
|
||||
&self,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
timestamp: u64,
|
||||
prev_randao: Hash256,
|
||||
finalized_block_hash: ExecutionBlockHash,
|
||||
suggested_fee_recipient: Address,
|
||||
) -> Result<Payload, Error> {
|
||||
self.get_full_payload_with(
|
||||
parent_hash,
|
||||
timestamp,
|
||||
prev_randao,
|
||||
finalized_block_hash,
|
||||
suggested_fee_recipient,
|
||||
noop,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn get_full_payload_with<Payload: ExecPayload<T>>(
|
||||
&self,
|
||||
parent_hash: ExecutionBlockHash,
|
||||
timestamp: u64,
|
||||
prev_randao: Hash256,
|
||||
finalized_block_hash: ExecutionBlockHash,
|
||||
suggested_fee_recipient: Address,
|
||||
f: fn(&ExecutionLayer<T>, &ExecutionPayload<T>) -> Option<ExecutionPayload<T>>,
|
||||
) -> Result<Payload, Error> {
|
||||
debug!(
|
||||
self.log(),
|
||||
"Issuing engine_getPayload";
|
||||
"suggested_fee_recipient" => ?suggested_fee_recipient,
|
||||
"prev_randao" => ?prev_randao,
|
||||
"timestamp" => timestamp,
|
||||
"parent_hash" => ?parent_hash,
|
||||
);
|
||||
self.engines()
|
||||
.first_success(|engine| async move {
|
||||
let payload_id = if let Some(id) = engine
|
||||
.get_payload_id(parent_hash, timestamp, prev_randao, suggested_fee_recipient)
|
||||
.await
|
||||
{
|
||||
// The payload id has been cached for this engine.
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_PRE_PREPARED_PAYLOAD_ID,
|
||||
&[metrics::HIT],
|
||||
);
|
||||
id
|
||||
} else {
|
||||
// The payload id has *not* been cached for this engine. Trigger an artificial
|
||||
// fork choice update to retrieve a payload ID.
|
||||
//
|
||||
// TODO(merge): a better algorithm might try to favour a node that already had a
|
||||
// cached payload id, since a payload that has had more time to produce is
|
||||
// likely to be more profitable.
|
||||
metrics::inc_counter_vec(
|
||||
&metrics::EXECUTION_LAYER_PRE_PREPARED_PAYLOAD_ID,
|
||||
&[metrics::MISS],
|
||||
);
|
||||
let fork_choice_state = ForkChoiceState {
|
||||
head_block_hash: parent_hash,
|
||||
safe_block_hash: parent_hash,
|
||||
finalized_block_hash,
|
||||
};
|
||||
let payload_attributes = PayloadAttributes {
|
||||
timestamp,
|
||||
prev_randao,
|
||||
suggested_fee_recipient,
|
||||
};
|
||||
|
||||
let response = engine
|
||||
.notify_forkchoice_updated(
|
||||
@@ -684,16 +734,19 @@ impl ExecutionLayer {
|
||||
}
|
||||
};
|
||||
|
||||
engine
|
||||
.api
|
||||
.get_payload_v1::<T>(payload_id)
|
||||
.await
|
||||
.map(Into::into)
|
||||
})
|
||||
engine
|
||||
.api
|
||||
.get_payload_v1::<T>(payload_id)
|
||||
.await
|
||||
.map_err(Error::EngineErrors)
|
||||
}
|
||||
}
|
||||
.map(|full_payload| {
|
||||
if f(self, &full_payload).is_some() {
|
||||
warn!(self.log(), "Duplicate payload cached, this might indicate redundant proposal attempts.");
|
||||
}
|
||||
full_payload.into()
|
||||
})
|
||||
})
|
||||
.await
|
||||
.map_err(Error::EngineErrors)
|
||||
}
|
||||
|
||||
/// Maps to the `engine_newPayload` JSON-RPC call.
|
||||
@@ -709,7 +762,7 @@ impl ExecutionLayer {
|
||||
/// - Invalid, if any nodes return invalid.
|
||||
/// - Syncing, if any nodes return syncing.
|
||||
/// - An error, if all nodes return an error.
|
||||
pub async fn notify_new_payload<T: EthSpec>(
|
||||
pub async fn notify_new_payload(
|
||||
&self,
|
||||
execution_payload: &ExecutionPayload<T>,
|
||||
) -> Result<PayloadStatus, Error> {
|
||||
@@ -872,23 +925,10 @@ impl ExecutionLayer {
|
||||
})
|
||||
.await;
|
||||
|
||||
// Only query builders with payload attributes populated.
|
||||
let builder_broadcast_results = if payload_attributes.is_some() {
|
||||
self.builders()
|
||||
.broadcast_without_retry(|engine| async move {
|
||||
engine
|
||||
.notify_forkchoice_updated(forkchoice_state, payload_attributes, self.log())
|
||||
.await
|
||||
})
|
||||
.await
|
||||
} else {
|
||||
vec![]
|
||||
};
|
||||
process_multiple_payload_statuses(
|
||||
head_block_hash,
|
||||
Some(broadcast_results)
|
||||
.into_iter()
|
||||
.chain(builder_broadcast_results.into_iter())
|
||||
.map(|result| result.map(|response| response.payload_status)),
|
||||
self.log(),
|
||||
)
|
||||
@@ -1147,7 +1187,7 @@ impl ExecutionLayer {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_payload_by_block_hash<T: EthSpec>(
|
||||
pub async fn get_payload_by_block_hash(
|
||||
&self,
|
||||
hash: ExecutionBlockHash,
|
||||
) -> Result<Option<ExecutionPayload<T>>, Error> {
|
||||
@@ -1160,7 +1200,7 @@ impl ExecutionLayer {
|
||||
.map_err(Error::EngineErrors)
|
||||
}
|
||||
|
||||
async fn get_payload_by_block_hash_from_engine<T: EthSpec>(
|
||||
async fn get_payload_by_block_hash_from_engine(
|
||||
&self,
|
||||
engine: &Engine<EngineApi>,
|
||||
hash: ExecutionBlockHash,
|
||||
@@ -1205,21 +1245,24 @@ impl ExecutionLayer {
|
||||
}))
|
||||
}
|
||||
|
||||
pub async fn propose_blinded_beacon_block<T: EthSpec>(
|
||||
pub async fn propose_blinded_beacon_block(
|
||||
&self,
|
||||
block: &SignedBeaconBlock<T, BlindedPayload<T>>,
|
||||
) -> Result<ExecutionPayload<T>, Error> {
|
||||
debug!(
|
||||
self.log(),
|
||||
"Issuing builder_proposeBlindedBlock";
|
||||
"Sending block to builder";
|
||||
"root" => ?block.canonical_root(),
|
||||
);
|
||||
self.builders()
|
||||
.first_success_without_retry(|engine| async move {
|
||||
engine.api.propose_blinded_block_v1(block.clone()).await
|
||||
})
|
||||
.await
|
||||
.map_err(Error::EngineErrors)
|
||||
if let Some(builder) = self.builder() {
|
||||
builder
|
||||
.post_builder_blinded_blocks(block)
|
||||
.await
|
||||
.map_err(Error::Builder)
|
||||
.map(|d| d.data)
|
||||
} else {
|
||||
Err(Error::NoPayloadBuilder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1320,3 +1363,7 @@ mod test {
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
fn noop<T: EthSpec>(_: &ExecutionLayer<T>, _: &ExecutionPayload<T>) -> Option<ExecutionPayload<T>> {
|
||||
None
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user