mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-09 11:41:51 +00:00
Encode Execution Engine Client Version In Graffiti (#5290)
* Add `engine_clientVersionV1` structs * Implement `engine_clientVersionV1` * Update to latest spec changes * Implement GraffitiCalculator Service * Added Unit Tests for GraffitiCalculator * Address Mac's Comments * Remove need to use clap in beacon chain * Merge remote-tracking branch 'upstream/unstable' into el_client_version_graffiti * Merge branch 'unstable' into el_client_version_graffiti # Conflicts: # beacon_node/beacon_chain/Cargo.toml
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
use super::*;
|
||||
use crate::auth::Auth;
|
||||
use crate::json_structures::*;
|
||||
use lazy_static::lazy_static;
|
||||
use lighthouse_version::{COMMIT_PREFIX, VERSION};
|
||||
use reqwest::header::CONTENT_TYPE;
|
||||
use sensitive_url::SensitiveUrl;
|
||||
use serde::de::DeserializeOwned;
|
||||
@@ -51,6 +53,9 @@ pub const ENGINE_GET_PAYLOAD_BODIES_TIMEOUT: Duration = Duration::from_secs(10);
|
||||
pub const ENGINE_EXCHANGE_CAPABILITIES: &str = "engine_exchangeCapabilities";
|
||||
pub const ENGINE_EXCHANGE_CAPABILITIES_TIMEOUT: Duration = Duration::from_secs(1);
|
||||
|
||||
pub const ENGINE_GET_CLIENT_VERSION_V1: &str = "engine_getClientVersionV1";
|
||||
pub const ENGINE_GET_CLIENT_VERSION_TIMEOUT: Duration = Duration::from_secs(1);
|
||||
|
||||
/// This error is returned during a `chainId` call by Geth.
|
||||
pub const EIP155_ERROR_STR: &str = "chain not synced beyond EIP-155 replay-protection fork block";
|
||||
/// This code is returned by all clients when a method is not supported
|
||||
@@ -69,8 +74,22 @@ pub static LIGHTHOUSE_CAPABILITIES: &[&str] = &[
|
||||
ENGINE_FORKCHOICE_UPDATED_V3,
|
||||
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
|
||||
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
|
||||
ENGINE_GET_CLIENT_VERSION_V1,
|
||||
];
|
||||
|
||||
lazy_static! {
|
||||
/// We opt to initialize the JsonClientVersionV1 rather than the ClientVersionV1
|
||||
/// for two reasons:
|
||||
/// 1. This saves the overhead of converting into Json for every engine call
|
||||
/// 2. The Json version lacks error checking so we can avoid calling `unwrap()`
|
||||
pub static ref LIGHTHOUSE_JSON_CLIENT_VERSION: JsonClientVersionV1 = JsonClientVersionV1 {
|
||||
code: ClientCode::Lighthouse.to_string(),
|
||||
name: "Lighthouse".to_string(),
|
||||
version: VERSION.replace("Lighthouse/", ""),
|
||||
commit: COMMIT_PREFIX.to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
/// Contains methods to convert arbitrary bytes to an ETH2 deposit contract object.
|
||||
pub mod deposit_log {
|
||||
use ssz::Decode;
|
||||
@@ -546,22 +565,21 @@ pub mod deposit_methods {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CapabilitiesCacheEntry {
|
||||
engine_capabilities: EngineCapabilities,
|
||||
fetch_time: Instant,
|
||||
pub struct CachedResponse<T: Clone> {
|
||||
pub data: T,
|
||||
pub fetch_time: Instant,
|
||||
}
|
||||
|
||||
impl CapabilitiesCacheEntry {
|
||||
pub fn new(engine_capabilities: EngineCapabilities) -> Self {
|
||||
impl<T: Clone> CachedResponse<T> {
|
||||
pub fn new(data: T) -> Self {
|
||||
Self {
|
||||
engine_capabilities,
|
||||
data,
|
||||
fetch_time: Instant::now(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn engine_capabilities(&self) -> EngineCapabilities {
|
||||
self.engine_capabilities
|
||||
pub fn data(&self) -> T {
|
||||
self.data.clone()
|
||||
}
|
||||
|
||||
pub fn age(&self) -> Duration {
|
||||
@@ -578,7 +596,8 @@ pub struct HttpJsonRpc {
|
||||
pub client: Client,
|
||||
pub url: SensitiveUrl,
|
||||
pub execution_timeout_multiplier: u32,
|
||||
pub engine_capabilities_cache: Mutex<Option<CapabilitiesCacheEntry>>,
|
||||
pub engine_capabilities_cache: Mutex<Option<CachedResponse<EngineCapabilities>>>,
|
||||
pub engine_version_cache: Mutex<Option<CachedResponse<Vec<ClientVersionV1>>>>,
|
||||
auth: Option<Auth>,
|
||||
}
|
||||
|
||||
@@ -592,6 +611,7 @@ impl HttpJsonRpc {
|
||||
url,
|
||||
execution_timeout_multiplier: execution_timeout_multiplier.unwrap_or(1),
|
||||
engine_capabilities_cache: Mutex::new(None),
|
||||
engine_version_cache: Mutex::new(None),
|
||||
auth: None,
|
||||
})
|
||||
}
|
||||
@@ -606,6 +626,7 @@ impl HttpJsonRpc {
|
||||
url,
|
||||
execution_timeout_multiplier: execution_timeout_multiplier.unwrap_or(1),
|
||||
engine_capabilities_cache: Mutex::new(None),
|
||||
engine_version_cache: Mutex::new(None),
|
||||
auth: Some(auth),
|
||||
})
|
||||
}
|
||||
@@ -1056,6 +1077,7 @@ impl HttpJsonRpc {
|
||||
get_payload_v1: capabilities.contains(ENGINE_GET_PAYLOAD_V1),
|
||||
get_payload_v2: capabilities.contains(ENGINE_GET_PAYLOAD_V2),
|
||||
get_payload_v3: capabilities.contains(ENGINE_GET_PAYLOAD_V3),
|
||||
get_client_version_v1: capabilities.contains(ENGINE_GET_CLIENT_VERSION_V1),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1078,15 +1100,78 @@ impl HttpJsonRpc {
|
||||
) -> Result<EngineCapabilities, Error> {
|
||||
let mut lock = self.engine_capabilities_cache.lock().await;
|
||||
|
||||
if let Some(lock) = lock.as_ref().filter(|entry| !entry.older_than(age_limit)) {
|
||||
Ok(lock.engine_capabilities())
|
||||
if let Some(lock) = lock
|
||||
.as_ref()
|
||||
.filter(|cached_response| !cached_response.older_than(age_limit))
|
||||
{
|
||||
Ok(lock.data())
|
||||
} else {
|
||||
let engine_capabilities = self.exchange_capabilities().await?;
|
||||
*lock = Some(CapabilitiesCacheEntry::new(engine_capabilities));
|
||||
*lock = Some(CachedResponse::new(engine_capabilities));
|
||||
Ok(engine_capabilities)
|
||||
}
|
||||
}
|
||||
|
||||
/// This method fetches the response from the engine without checking
|
||||
/// any caches or storing the result in the cache. It is better to use
|
||||
/// `get_engine_version(Some(Duration::ZERO))` if you want to force
|
||||
/// fetching from the EE as this will cache the result.
|
||||
pub async fn get_client_version_v1(&self) -> Result<Vec<ClientVersionV1>, Error> {
|
||||
let params = json!([*LIGHTHOUSE_JSON_CLIENT_VERSION]);
|
||||
|
||||
let response: Vec<JsonClientVersionV1> = self
|
||||
.rpc_request(
|
||||
ENGINE_GET_CLIENT_VERSION_V1,
|
||||
params,
|
||||
ENGINE_GET_CLIENT_VERSION_TIMEOUT * self.execution_timeout_multiplier,
|
||||
)
|
||||
.await?;
|
||||
|
||||
response
|
||||
.into_iter()
|
||||
.map(TryInto::try_into)
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.map_err(Error::InvalidClientVersion)
|
||||
}
|
||||
|
||||
pub async fn clear_engine_version_cache(&self) {
|
||||
*self.engine_version_cache.lock().await = None;
|
||||
}
|
||||
|
||||
/// Returns the execution engine version resulting from a call to
|
||||
/// engine_getClientVersionV1. If the version cache is not populated, or if it
|
||||
/// is populated with a cached result of age >= `age_limit`, this method will
|
||||
/// fetch the result from the execution engine and populate the cache before
|
||||
/// returning it. Otherwise it will return the cached result from an earlier
|
||||
/// call.
|
||||
///
|
||||
/// Set `age_limit` to `None` to always return the cached result
|
||||
/// Set `age_limit` to `Some(Duration::ZERO)` to force fetching from EE
|
||||
pub async fn get_engine_version(
|
||||
&self,
|
||||
age_limit: Option<Duration>,
|
||||
) -> Result<Vec<ClientVersionV1>, Error> {
|
||||
// check engine capabilities first (avoids holding two locks at once)
|
||||
let engine_capabilities = self.get_engine_capabilities(None).await?;
|
||||
if !engine_capabilities.get_client_version_v1 {
|
||||
// We choose an empty vec to denote that this method is not
|
||||
// supported instead of an error since this method is optional
|
||||
// & we don't want to log a warning and concern the user
|
||||
return Ok(vec![]);
|
||||
}
|
||||
let mut lock = self.engine_version_cache.lock().await;
|
||||
if let Some(lock) = lock
|
||||
.as_ref()
|
||||
.filter(|cached_response| !cached_response.older_than(age_limit))
|
||||
{
|
||||
Ok(lock.data())
|
||||
} else {
|
||||
let engine_version = self.get_client_version_v1().await?;
|
||||
*lock = Some(CachedResponse::new(engine_version.clone()));
|
||||
Ok(engine_version)
|
||||
}
|
||||
}
|
||||
|
||||
// automatically selects the latest version of
|
||||
// new_payload that the execution engine supports
|
||||
pub async fn new_payload<E: EthSpec>(
|
||||
|
||||
Reference in New Issue
Block a user