2021-09-29 22:14:15 +00:00
|
|
|
//! Provides generic behaviour for multiple execution engines, specifically fallback behaviour.
|
|
|
|
|
2022-02-17 21:47:06 +00:00
|
|
|
use crate::engine_api::{
|
2022-03-31 07:52:23 +00:00
|
|
|
Builder, EngineApi, Error as EngineApiError, ForkchoiceUpdatedResponse, PayloadAttributes,
|
|
|
|
PayloadId,
|
2022-02-17 21:47:06 +00:00
|
|
|
};
|
2022-03-31 07:52:23 +00:00
|
|
|
use crate::{BuilderApi, HttpJsonRpc};
|
|
|
|
use async_trait::async_trait;
|
2021-09-29 22:14:15 +00:00
|
|
|
use futures::future::join_all;
|
2021-11-15 06:13:38 +00:00
|
|
|
use lru::LruCache;
|
2021-10-06 13:34:17 +00:00
|
|
|
use slog::{crit, debug, info, warn, Logger};
|
2021-09-29 22:14:15 +00:00
|
|
|
use std::future::Future;
|
2021-11-15 06:13:38 +00:00
|
|
|
use tokio::sync::{Mutex, RwLock};
|
2022-02-28 22:07:48 +00:00
|
|
|
use types::{Address, ExecutionBlockHash, Hash256};
|
2021-11-15 06:13:38 +00:00
|
|
|
|
|
|
|
/// The number of payload IDs that will be stored for each `Engine`.
|
|
|
|
///
|
|
|
|
/// Since the size of each value is small (~100 bytes) a large number is used for safety.
|
|
|
|
const PAYLOAD_ID_LRU_CACHE_SIZE: usize = 512;
|
2021-09-29 22:14:15 +00:00
|
|
|
|
|
|
|
/// Stores the remembered state of a engine.
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
|
|
enum EngineState {
|
2021-10-06 10:21:21 +00:00
|
|
|
Synced,
|
2021-09-29 22:14:15 +00:00
|
|
|
Offline,
|
2021-10-06 10:21:21 +00:00
|
|
|
Syncing,
|
2022-03-08 06:46:24 +00:00
|
|
|
AuthFailed,
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
|
|
|
|
2021-10-06 10:21:21 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
2021-11-15 06:13:38 +00:00
|
|
|
pub struct ForkChoiceState {
|
2022-02-28 22:07:48 +00:00
|
|
|
pub head_block_hash: ExecutionBlockHash,
|
|
|
|
pub safe_block_hash: ExecutionBlockHash,
|
|
|
|
pub finalized_block_hash: ExecutionBlockHash,
|
2021-10-06 10:21:21 +00:00
|
|
|
}
|
2021-09-29 22:14:15 +00:00
|
|
|
|
2021-10-06 10:21:21 +00:00
|
|
|
/// Used to enable/disable logging on some tasks.
|
|
|
|
#[derive(Copy, Clone, PartialEq)]
|
|
|
|
pub enum Logging {
|
|
|
|
Enabled,
|
|
|
|
Disabled,
|
|
|
|
}
|
2021-09-29 22:14:15 +00:00
|
|
|
|
2021-10-06 10:21:21 +00:00
|
|
|
impl Logging {
|
|
|
|
pub fn is_enabled(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Logging::Enabled => true,
|
|
|
|
Logging::Disabled => false,
|
|
|
|
}
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-15 06:13:38 +00:00
|
|
|
#[derive(Hash, PartialEq, std::cmp::Eq)]
|
|
|
|
struct PayloadIdCacheKey {
|
2022-02-28 22:07:48 +00:00
|
|
|
pub head_block_hash: ExecutionBlockHash,
|
2021-11-15 06:13:38 +00:00
|
|
|
pub timestamp: u64,
|
2022-03-03 02:10:57 +00:00
|
|
|
pub prev_randao: Hash256,
|
2021-12-12 09:04:21 +00:00
|
|
|
pub suggested_fee_recipient: Address,
|
2021-11-15 06:13:38 +00:00
|
|
|
}
|
|
|
|
|
2021-09-29 22:14:15 +00:00
|
|
|
/// An execution engine.
|
|
|
|
pub struct Engine<T> {
|
|
|
|
pub id: String,
|
2022-03-31 07:52:23 +00:00
|
|
|
pub api: HttpJsonRpc<T>,
|
2021-11-15 06:13:38 +00:00
|
|
|
payload_id_cache: Mutex<LruCache<PayloadIdCacheKey, PayloadId>>,
|
2021-09-29 22:14:15 +00:00
|
|
|
state: RwLock<EngineState>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<T> Engine<T> {
|
|
|
|
/// Creates a new, offline engine.
|
2022-03-31 07:52:23 +00:00
|
|
|
pub fn new(id: String, api: HttpJsonRpc<T>) -> Self {
|
2021-09-29 22:14:15 +00:00
|
|
|
Self {
|
|
|
|
id,
|
|
|
|
api,
|
2021-11-15 06:13:38 +00:00
|
|
|
payload_id_cache: Mutex::new(LruCache::new(PAYLOAD_ID_LRU_CACHE_SIZE)),
|
2021-09-29 22:14:15 +00:00
|
|
|
state: RwLock::new(EngineState::Offline),
|
|
|
|
}
|
|
|
|
}
|
2021-11-15 06:13:38 +00:00
|
|
|
|
|
|
|
pub async fn get_payload_id(
|
|
|
|
&self,
|
2022-02-28 22:07:48 +00:00
|
|
|
head_block_hash: ExecutionBlockHash,
|
2021-11-15 06:13:38 +00:00
|
|
|
timestamp: u64,
|
2022-03-03 02:10:57 +00:00
|
|
|
prev_randao: Hash256,
|
2021-12-12 09:04:21 +00:00
|
|
|
suggested_fee_recipient: Address,
|
2021-11-15 06:13:38 +00:00
|
|
|
) -> Option<PayloadId> {
|
|
|
|
self.payload_id_cache
|
|
|
|
.lock()
|
|
|
|
.await
|
|
|
|
.get(&PayloadIdCacheKey {
|
|
|
|
head_block_hash,
|
|
|
|
timestamp,
|
2022-03-03 02:10:57 +00:00
|
|
|
prev_randao,
|
2021-12-12 09:04:21 +00:00
|
|
|
suggested_fee_recipient,
|
2021-11-15 06:13:38 +00:00
|
|
|
})
|
|
|
|
.cloned()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-31 07:52:23 +00:00
|
|
|
#[async_trait]
|
|
|
|
impl Builder for Engine<EngineApi> {
|
|
|
|
async fn notify_forkchoice_updated(
|
2021-11-15 06:13:38 +00:00
|
|
|
&self,
|
|
|
|
forkchoice_state: ForkChoiceState,
|
|
|
|
payload_attributes: Option<PayloadAttributes>,
|
|
|
|
log: &Logger,
|
2022-02-17 21:47:06 +00:00
|
|
|
) -> Result<ForkchoiceUpdatedResponse, EngineApiError> {
|
2021-11-15 06:13:38 +00:00
|
|
|
let response = self
|
|
|
|
.api
|
|
|
|
.forkchoice_updated_v1(forkchoice_state, payload_attributes)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
if let Some(payload_id) = response.payload_id {
|
|
|
|
if let Some(key) =
|
|
|
|
payload_attributes.map(|pa| PayloadIdCacheKey::new(&forkchoice_state, &pa))
|
|
|
|
{
|
|
|
|
self.payload_id_cache.lock().await.put(key, payload_id);
|
|
|
|
} else {
|
|
|
|
debug!(
|
|
|
|
log,
|
|
|
|
"Engine returned unexpected payload_id";
|
|
|
|
"payload_id" => ?payload_id
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-17 21:47:06 +00:00
|
|
|
Ok(response)
|
2021-11-15 06:13:38 +00:00
|
|
|
}
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
|
|
|
|
2022-03-31 07:52:23 +00:00
|
|
|
#[async_trait]
|
|
|
|
impl Builder for Engine<BuilderApi> {
|
|
|
|
async fn notify_forkchoice_updated(
|
|
|
|
&self,
|
|
|
|
forkchoice_state: ForkChoiceState,
|
|
|
|
pa: Option<PayloadAttributes>,
|
|
|
|
log: &Logger,
|
|
|
|
) -> Result<ForkchoiceUpdatedResponse, EngineApiError> {
|
|
|
|
let payload_attributes = pa.ok_or(EngineApiError::InvalidBuilderQuery)?;
|
|
|
|
let response = self
|
|
|
|
.api
|
|
|
|
.forkchoice_updated_v1(forkchoice_state, Some(payload_attributes))
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
if let Some(payload_id) = response.payload_id {
|
|
|
|
let key = PayloadIdCacheKey::new(&forkchoice_state, &payload_attributes);
|
|
|
|
self.payload_id_cache.lock().await.put(key, payload_id);
|
|
|
|
} else {
|
|
|
|
warn!(
|
|
|
|
log,
|
|
|
|
"Builder should have returned a payload_id for attributes {:?}", payload_attributes
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(response)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-22 14:27:16 +00:00
|
|
|
// This structure used to hold multiple execution engines managed in a fallback manner. This
|
|
|
|
// functionality has been removed following https://github.com/sigp/lighthouse/issues/3118 and this
|
|
|
|
// struct will likely be removed in the future.
|
2022-03-31 07:52:23 +00:00
|
|
|
pub struct Engines {
|
2022-06-22 14:27:16 +00:00
|
|
|
pub engine: Engine<EngineApi>,
|
2021-11-15 06:13:38 +00:00
|
|
|
pub latest_forkchoice_state: RwLock<Option<ForkChoiceState>>,
|
2021-09-29 22:14:15 +00:00
|
|
|
pub log: Logger,
|
|
|
|
}
|
|
|
|
|
2022-03-31 07:52:23 +00:00
|
|
|
pub struct Builders {
|
|
|
|
pub builders: Vec<Engine<BuilderApi>>,
|
|
|
|
pub log: Logger,
|
|
|
|
}
|
|
|
|
|
2021-09-29 22:14:15 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum EngineError {
|
|
|
|
Offline { id: String },
|
|
|
|
Api { id: String, error: EngineApiError },
|
2022-03-08 06:46:24 +00:00
|
|
|
Auth { id: String },
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
|
|
|
|
2022-03-31 07:52:23 +00:00
|
|
|
impl Engines {
|
2021-11-15 06:13:38 +00:00
|
|
|
async fn get_latest_forkchoice_state(&self) -> Option<ForkChoiceState> {
|
|
|
|
*self.latest_forkchoice_state.read().await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn set_latest_forkchoice_state(&self, state: ForkChoiceState) {
|
|
|
|
*self.latest_forkchoice_state.write().await = Some(state);
|
2021-10-06 10:21:21 +00:00
|
|
|
}
|
|
|
|
|
2022-06-22 14:27:16 +00:00
|
|
|
async fn send_latest_forkchoice_state(&self) {
|
2021-11-15 06:13:38 +00:00
|
|
|
let latest_forkchoice_state = self.get_latest_forkchoice_state().await;
|
|
|
|
|
|
|
|
if let Some(forkchoice_state) = latest_forkchoice_state {
|
2022-03-09 00:42:05 +00:00
|
|
|
if forkchoice_state.head_block_hash == ExecutionBlockHash::zero() {
|
|
|
|
debug!(
|
|
|
|
self.log,
|
|
|
|
"No need to call forkchoiceUpdated";
|
|
|
|
"msg" => "head does not have execution enabled",
|
2022-06-22 14:27:16 +00:00
|
|
|
"id" => &self.engine.id,
|
2022-03-09 00:42:05 +00:00
|
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-10-06 10:21:21 +00:00
|
|
|
info!(
|
|
|
|
self.log,
|
|
|
|
"Issuing forkchoiceUpdated";
|
2021-11-15 06:13:38 +00:00
|
|
|
"forkchoice_state" => ?forkchoice_state,
|
2022-06-22 14:27:16 +00:00
|
|
|
"id" => &self.engine.id,
|
2021-10-06 10:21:21 +00:00
|
|
|
);
|
|
|
|
|
2021-11-15 06:13:38 +00:00
|
|
|
// For simplicity, payload attributes are never included in this call. It may be
|
|
|
|
// reasonable to include them in the future.
|
2022-06-22 14:27:16 +00:00
|
|
|
if let Err(e) = self
|
|
|
|
.engine
|
2021-10-06 10:21:21 +00:00
|
|
|
.api
|
2021-11-15 06:13:38 +00:00
|
|
|
.forkchoice_updated_v1(forkchoice_state, None)
|
2021-10-06 10:21:21 +00:00
|
|
|
.await
|
|
|
|
{
|
2021-10-06 13:34:17 +00:00
|
|
|
debug!(
|
2021-10-06 10:21:21 +00:00
|
|
|
self.log,
|
|
|
|
"Failed to issue latest head to engine";
|
|
|
|
"error" => ?e,
|
2022-06-22 14:27:16 +00:00
|
|
|
"id" => &self.engine.id,
|
2021-10-06 10:21:21 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
debug!(
|
|
|
|
self.log,
|
|
|
|
"No head, not sending to engine";
|
2022-06-22 14:27:16 +00:00
|
|
|
"id" => &self.engine.id,
|
2021-10-06 10:21:21 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-22 14:27:16 +00:00
|
|
|
/// Returns `true` if the engine has a "synced" status.
|
|
|
|
pub async fn is_synced(&self) -> bool {
|
|
|
|
*self.engine.state.read().await == EngineState::Synced
|
2021-10-06 10:21:21 +00:00
|
|
|
}
|
2022-06-22 14:27:16 +00:00
|
|
|
/// Run the `EngineApi::upcheck` function if the node's last known state is not synced. This
|
|
|
|
/// might be used to recover the node if offline.
|
2021-10-06 10:21:21 +00:00
|
|
|
pub async fn upcheck_not_synced(&self, logging: Logging) {
|
2022-06-22 14:27:16 +00:00
|
|
|
let mut state_lock = self.engine.state.write().await;
|
|
|
|
if *state_lock != EngineState::Synced {
|
|
|
|
match self.engine.api.upcheck().await {
|
|
|
|
Ok(()) => {
|
|
|
|
if logging.is_enabled() {
|
|
|
|
info!(
|
|
|
|
self.log,
|
|
|
|
"Execution engine online";
|
|
|
|
);
|
2021-10-06 10:21:21 +00:00
|
|
|
}
|
2022-06-22 14:27:16 +00:00
|
|
|
// Send the node our latest forkchoice_state.
|
|
|
|
self.send_latest_forkchoice_state().await;
|
|
|
|
|
|
|
|
*state_lock = EngineState::Synced
|
|
|
|
}
|
|
|
|
Err(EngineApiError::IsSyncing) => {
|
|
|
|
if logging.is_enabled() {
|
|
|
|
warn!(
|
|
|
|
self.log,
|
|
|
|
"Execution engine syncing";
|
|
|
|
)
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
2022-06-22 14:27:16 +00:00
|
|
|
|
|
|
|
// Send the node our latest forkchoice_state, it may assist with syncing.
|
|
|
|
self.send_latest_forkchoice_state().await;
|
|
|
|
|
|
|
|
*state_lock = EngineState::Syncing
|
|
|
|
}
|
|
|
|
Err(EngineApiError::Auth(err)) => {
|
|
|
|
if logging.is_enabled() {
|
|
|
|
warn!(
|
|
|
|
self.log,
|
|
|
|
"Failed jwt authorization";
|
|
|
|
"error" => ?err,
|
|
|
|
);
|
2022-03-08 06:46:24 +00:00
|
|
|
}
|
2022-06-22 14:27:16 +00:00
|
|
|
|
|
|
|
*state_lock = EngineState::AuthFailed
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
if logging.is_enabled() {
|
|
|
|
warn!(
|
|
|
|
self.log,
|
|
|
|
"Execution engine offline";
|
|
|
|
"error" => ?e,
|
|
|
|
)
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-06-22 14:27:16 +00:00
|
|
|
}
|
2021-09-29 22:14:15 +00:00
|
|
|
|
2022-06-22 14:27:16 +00:00
|
|
|
if *state_lock != EngineState::Synced && logging.is_enabled() {
|
2021-09-29 22:14:15 +00:00
|
|
|
crit!(
|
|
|
|
self.log,
|
2021-10-06 10:21:21 +00:00
|
|
|
"No synced execution engines";
|
2021-09-29 22:14:15 +00:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Run `func` on all engines, in the order in which they are defined, returning the first
|
|
|
|
/// successful result that is found.
|
|
|
|
///
|
|
|
|
/// This function might try to run `func` twice. If all nodes return an error on the first time
|
|
|
|
/// it runs, it will try to upcheck all offline nodes and then run the function again.
|
|
|
|
pub async fn first_success<'a, F, G, H>(&'a self, func: F) -> Result<H, Vec<EngineError>>
|
|
|
|
where
|
2022-03-31 07:52:23 +00:00
|
|
|
F: Fn(&'a Engine<EngineApi>) -> G + Copy,
|
2021-09-29 22:14:15 +00:00
|
|
|
G: Future<Output = Result<H, EngineApiError>>,
|
|
|
|
{
|
|
|
|
match self.first_success_without_retry(func).await {
|
|
|
|
Ok(result) => Ok(result),
|
|
|
|
Err(mut first_errors) => {
|
|
|
|
// Try to recover some nodes.
|
2021-10-06 10:21:21 +00:00
|
|
|
self.upcheck_not_synced(Logging::Enabled).await;
|
2021-09-29 22:14:15 +00:00
|
|
|
// Retry the call on all nodes.
|
|
|
|
match self.first_success_without_retry(func).await {
|
|
|
|
Ok(result) => Ok(result),
|
|
|
|
Err(second_errors) => {
|
|
|
|
first_errors.extend(second_errors);
|
|
|
|
Err(first_errors)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Run `func` on all engines, in the order in which they are defined, returning the first
|
|
|
|
/// successful result that is found.
|
2022-03-31 07:52:23 +00:00
|
|
|
pub async fn first_success_without_retry<'a, F, G, H>(
|
2021-09-29 22:14:15 +00:00
|
|
|
&'a self,
|
|
|
|
func: F,
|
|
|
|
) -> Result<H, Vec<EngineError>>
|
|
|
|
where
|
2022-03-31 07:52:23 +00:00
|
|
|
F: Fn(&'a Engine<EngineApi>) -> G,
|
2021-09-29 22:14:15 +00:00
|
|
|
G: Future<Output = Result<H, EngineApiError>>,
|
|
|
|
{
|
|
|
|
let mut errors = vec![];
|
|
|
|
|
2022-06-22 14:27:16 +00:00
|
|
|
let (engine_synced, engine_auth_failed) = {
|
|
|
|
let state = self.engine.state.read().await;
|
|
|
|
(
|
|
|
|
*state == EngineState::Synced,
|
|
|
|
*state == EngineState::AuthFailed,
|
|
|
|
)
|
|
|
|
};
|
|
|
|
if engine_synced {
|
|
|
|
match func(&self.engine).await {
|
|
|
|
Ok(result) => return Ok(result),
|
|
|
|
Err(error) => {
|
|
|
|
debug!(
|
|
|
|
self.log,
|
|
|
|
"Execution engine call failed";
|
|
|
|
"error" => ?error,
|
|
|
|
"id" => &&self.engine.id
|
|
|
|
);
|
|
|
|
*self.engine.state.write().await = EngineState::Offline;
|
|
|
|
errors.push(EngineError::Api {
|
|
|
|
id: self.engine.id.clone(),
|
|
|
|
error,
|
|
|
|
})
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-22 14:27:16 +00:00
|
|
|
} else if engine_auth_failed {
|
|
|
|
errors.push(EngineError::Auth {
|
|
|
|
id: self.engine.id.clone(),
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
errors.push(EngineError::Offline {
|
|
|
|
id: self.engine.id.clone(),
|
|
|
|
})
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Err(errors)
|
|
|
|
}
|
|
|
|
|
2022-06-22 14:27:16 +00:00
|
|
|
/// Runs `func` on the node.
|
2021-09-29 22:14:15 +00:00
|
|
|
///
|
|
|
|
/// This function might try to run `func` twice. If all nodes return an error on the first time
|
|
|
|
/// it runs, it will try to upcheck all offline nodes and then run the function again.
|
2022-06-22 14:27:16 +00:00
|
|
|
pub async fn broadcast<'a, F, G, H>(&'a self, func: F) -> Result<H, EngineError>
|
2021-09-29 22:14:15 +00:00
|
|
|
where
|
2022-03-31 07:52:23 +00:00
|
|
|
F: Fn(&'a Engine<EngineApi>) -> G + Copy,
|
2021-09-29 22:14:15 +00:00
|
|
|
G: Future<Output = Result<H, EngineApiError>>,
|
|
|
|
{
|
2022-06-22 14:27:16 +00:00
|
|
|
match self.broadcast_without_retry(func).await {
|
|
|
|
Err(EngineError::Offline { .. }) => {
|
|
|
|
self.upcheck_not_synced(Logging::Enabled).await;
|
|
|
|
self.broadcast_without_retry(func).await
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
2022-06-22 14:27:16 +00:00
|
|
|
other => other,
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-22 14:27:16 +00:00
|
|
|
/// Runs `func` on the node if it's last state is not offline.
|
|
|
|
pub async fn broadcast_without_retry<'a, F, G, H>(&'a self, func: F) -> Result<H, EngineError>
|
2021-09-29 22:14:15 +00:00
|
|
|
where
|
2022-03-31 07:52:23 +00:00
|
|
|
F: Fn(&'a Engine<EngineApi>) -> G,
|
2021-09-29 22:14:15 +00:00
|
|
|
G: Future<Output = Result<H, EngineApiError>>,
|
|
|
|
{
|
|
|
|
let func = &func;
|
2022-06-22 14:27:16 +00:00
|
|
|
if *self.engine.state.read().await == EngineState::Offline {
|
|
|
|
Err(EngineError::Offline {
|
|
|
|
id: self.engine.id.clone(),
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
match func(&self.engine).await {
|
|
|
|
Ok(res) => Ok(res),
|
|
|
|
Err(error) => {
|
|
|
|
debug!(
|
|
|
|
self.log,
|
|
|
|
"Execution engine call failed";
|
|
|
|
"error" => ?error,
|
|
|
|
);
|
|
|
|
*self.engine.state.write().await = EngineState::Offline;
|
|
|
|
Err(EngineError::Api {
|
|
|
|
id: self.engine.id.clone(),
|
|
|
|
error,
|
|
|
|
})
|
2022-03-28 23:32:11 +00:00
|
|
|
}
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
2022-06-22 14:27:16 +00:00
|
|
|
}
|
2021-09-29 22:14:15 +00:00
|
|
|
}
|
|
|
|
}
|
2021-11-15 06:13:38 +00:00
|
|
|
|
2022-03-31 07:52:23 +00:00
|
|
|
impl Builders {
|
|
|
|
pub async fn first_success_without_retry<'a, F, G, H>(
|
|
|
|
&'a self,
|
|
|
|
func: F,
|
|
|
|
) -> Result<H, Vec<EngineError>>
|
|
|
|
where
|
|
|
|
F: Fn(&'a Engine<BuilderApi>) -> G,
|
|
|
|
G: Future<Output = Result<H, EngineApiError>>,
|
|
|
|
{
|
|
|
|
let mut errors = vec![];
|
|
|
|
|
|
|
|
for builder in &self.builders {
|
|
|
|
match func(builder).await {
|
|
|
|
Ok(result) => return Ok(result),
|
|
|
|
Err(error) => {
|
|
|
|
debug!(
|
|
|
|
self.log,
|
|
|
|
"Builder call failed";
|
|
|
|
"error" => ?error,
|
|
|
|
"id" => &builder.id
|
|
|
|
);
|
|
|
|
errors.push(EngineError::Api {
|
|
|
|
id: builder.id.clone(),
|
|
|
|
error,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Err(errors)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn broadcast_without_retry<'a, F, G, H>(
|
|
|
|
&'a self,
|
|
|
|
func: F,
|
|
|
|
) -> Vec<Result<H, EngineError>>
|
|
|
|
where
|
|
|
|
F: Fn(&'a Engine<BuilderApi>) -> G,
|
|
|
|
G: Future<Output = Result<H, EngineApiError>>,
|
|
|
|
{
|
|
|
|
let func = &func;
|
|
|
|
let futures = self.builders.iter().map(|engine| async move {
|
|
|
|
func(engine).await.map_err(|error| {
|
|
|
|
debug!(
|
|
|
|
self.log,
|
|
|
|
"Builder call failed";
|
|
|
|
"error" => ?error,
|
|
|
|
"id" => &engine.id
|
|
|
|
);
|
|
|
|
EngineError::Api {
|
|
|
|
id: engine.id.clone(),
|
|
|
|
error,
|
|
|
|
}
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
join_all(futures).await
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-15 06:13:38 +00:00
|
|
|
impl PayloadIdCacheKey {
|
|
|
|
fn new(state: &ForkChoiceState, attributes: &PayloadAttributes) -> Self {
|
|
|
|
Self {
|
|
|
|
head_block_hash: state.head_block_hash,
|
|
|
|
timestamp: attributes.timestamp,
|
2022-03-03 02:10:57 +00:00
|
|
|
prev_randao: attributes.prev_randao,
|
2021-12-12 09:04:21 +00:00
|
|
|
suggested_fee_recipient: attributes.suggested_fee_recipient,
|
2021-11-15 06:13:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|