mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-06 10:11:44 +00:00
Implement engine API v1.0.0-alpha.4 (#2810)
* Added ForkchoiceUpdatedV1 & GetPayloadV1 * Added ExecutePayloadV1 * Added new geth test vectors * Separated Json Object/Serialization Code into file * Deleted code/tests for Requests Removed from spec * Finally fixed serialization of null '0x' * Made Naming of JSON Structs Consistent * Fix clippy lints * Remove u64 payload id * Remove unused serde impls * Swap to [u8; 8] for payload id * Tidy * Adjust some block gen return vals * Tidy * Add fallback when payload id is unknown * Remove comment Co-authored-by: Mark Mackey <mark@sigmaprime.io>
This commit is contained in:
@@ -1,11 +1,17 @@
|
||||
//! Provides generic behaviour for multiple execution engines, specifically fallback behaviour.
|
||||
|
||||
use crate::engine_api::{EngineApi, Error as EngineApiError};
|
||||
use crate::engine_api::{EngineApi, Error as EngineApiError, PayloadAttributes, PayloadId};
|
||||
use futures::future::join_all;
|
||||
use lru::LruCache;
|
||||
use slog::{crit, debug, info, warn, Logger};
|
||||
use std::future::Future;
|
||||
use tokio::sync::RwLock;
|
||||
use types::Hash256;
|
||||
use tokio::sync::{Mutex, RwLock};
|
||||
use types::{Address, Hash256};
|
||||
|
||||
/// 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;
|
||||
|
||||
/// Stores the remembered state of a engine.
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
@@ -16,8 +22,9 @@ enum EngineState {
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||
pub struct ForkChoiceHead {
|
||||
pub struct ForkChoiceState {
|
||||
pub head_block_hash: Hash256,
|
||||
pub safe_block_hash: Hash256,
|
||||
pub finalized_block_hash: Hash256,
|
||||
}
|
||||
|
||||
@@ -37,10 +44,19 @@ impl Logging {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, std::cmp::Eq)]
|
||||
struct PayloadIdCacheKey {
|
||||
pub head_block_hash: Hash256,
|
||||
pub timestamp: u64,
|
||||
pub random: Hash256,
|
||||
pub fee_recipient: Address,
|
||||
}
|
||||
|
||||
/// An execution engine.
|
||||
pub struct Engine<T> {
|
||||
pub id: String,
|
||||
pub api: T,
|
||||
payload_id_cache: Mutex<LruCache<PayloadIdCacheKey, PayloadId>>,
|
||||
state: RwLock<EngineState>,
|
||||
}
|
||||
|
||||
@@ -50,16 +66,66 @@ impl<T> Engine<T> {
|
||||
Self {
|
||||
id,
|
||||
api,
|
||||
payload_id_cache: Mutex::new(LruCache::new(PAYLOAD_ID_LRU_CACHE_SIZE)),
|
||||
state: RwLock::new(EngineState::Offline),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_payload_id(
|
||||
&self,
|
||||
head_block_hash: Hash256,
|
||||
timestamp: u64,
|
||||
random: Hash256,
|
||||
fee_recipient: Address,
|
||||
) -> Option<PayloadId> {
|
||||
self.payload_id_cache
|
||||
.lock()
|
||||
.await
|
||||
.get(&PayloadIdCacheKey {
|
||||
head_block_hash,
|
||||
timestamp,
|
||||
random,
|
||||
fee_recipient,
|
||||
})
|
||||
.cloned()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: EngineApi> Engine<T> {
|
||||
pub async fn notify_forkchoice_updated(
|
||||
&self,
|
||||
forkchoice_state: ForkChoiceState,
|
||||
payload_attributes: Option<PayloadAttributes>,
|
||||
log: &Logger,
|
||||
) -> Result<Option<PayloadId>, EngineApiError> {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(response.payload_id)
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds multiple execution engines and provides functionality for managing them in a fallback
|
||||
/// manner.
|
||||
pub struct Engines<T> {
|
||||
pub engines: Vec<Engine<T>>,
|
||||
pub latest_head: RwLock<Option<ForkChoiceHead>>,
|
||||
pub latest_forkchoice_state: RwLock<Option<ForkChoiceState>>,
|
||||
pub log: Logger,
|
||||
}
|
||||
|
||||
@@ -70,23 +136,30 @@ pub enum EngineError {
|
||||
}
|
||||
|
||||
impl<T: EngineApi> Engines<T> {
|
||||
pub async fn set_latest_head(&self, latest_head: ForkChoiceHead) {
|
||||
*self.latest_head.write().await = Some(latest_head);
|
||||
async fn get_latest_forkchoice_state(&self) -> Option<ForkChoiceState> {
|
||||
*self.latest_forkchoice_state.read().await
|
||||
}
|
||||
|
||||
async fn send_latest_head(&self, engine: &Engine<T>) {
|
||||
let latest_head: Option<ForkChoiceHead> = *self.latest_head.read().await;
|
||||
if let Some(head) = latest_head {
|
||||
pub async fn set_latest_forkchoice_state(&self, state: ForkChoiceState) {
|
||||
*self.latest_forkchoice_state.write().await = Some(state);
|
||||
}
|
||||
|
||||
async fn send_latest_forkchoice_state(&self, engine: &Engine<T>) {
|
||||
let latest_forkchoice_state = self.get_latest_forkchoice_state().await;
|
||||
|
||||
if let Some(forkchoice_state) = latest_forkchoice_state {
|
||||
info!(
|
||||
self.log,
|
||||
"Issuing forkchoiceUpdated";
|
||||
"head" => ?head,
|
||||
"forkchoice_state" => ?forkchoice_state,
|
||||
"id" => &engine.id,
|
||||
);
|
||||
|
||||
// For simplicity, payload attributes are never included in this call. It may be
|
||||
// reasonable to include them in the future.
|
||||
if let Err(e) = engine
|
||||
.api
|
||||
.forkchoice_updated(head.head_block_hash, head.finalized_block_hash)
|
||||
.forkchoice_updated_v1(forkchoice_state, None)
|
||||
.await
|
||||
{
|
||||
debug!(
|
||||
@@ -132,8 +205,8 @@ impl<T: EngineApi> Engines<T> {
|
||||
);
|
||||
}
|
||||
|
||||
// Send the node our latest head.
|
||||
self.send_latest_head(engine).await;
|
||||
// Send the node our latest forkchoice_state.
|
||||
self.send_latest_forkchoice_state(engine).await;
|
||||
|
||||
*state_lock = EngineState::Synced
|
||||
}
|
||||
@@ -146,8 +219,8 @@ impl<T: EngineApi> Engines<T> {
|
||||
)
|
||||
}
|
||||
|
||||
// Send the node our latest head, it may assist with syncing.
|
||||
self.send_latest_head(engine).await;
|
||||
// Send the node our latest forkchoice_state, it may assist with syncing.
|
||||
self.send_latest_forkchoice_state(engine).await;
|
||||
|
||||
*state_lock = EngineState::Syncing
|
||||
}
|
||||
@@ -312,3 +385,14 @@ impl<T: EngineApi> Engines<T> {
|
||||
join_all(futures).await
|
||||
}
|
||||
}
|
||||
|
||||
impl PayloadIdCacheKey {
|
||||
fn new(state: &ForkChoiceState, attributes: &PayloadAttributes) -> Self {
|
||||
Self {
|
||||
head_block_hash: state.head_block_hash,
|
||||
timestamp: attributes.timestamp,
|
||||
random: attributes.random,
|
||||
fee_recipient: attributes.fee_recipient,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user