mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-09 19:51:47 +00:00
Resolve merge conflicts
This commit is contained in:
@@ -22,7 +22,6 @@ use eth2::types::{ForkVersionedResponse, builder::SignedBuilderBid};
|
||||
use fixed_bytes::UintExtended;
|
||||
use fork_choice::ForkchoiceUpdateParameters;
|
||||
use logging::crit;
|
||||
use lru::LruCache;
|
||||
pub use payload_status::PayloadStatus;
|
||||
use payload_status::process_payload_status;
|
||||
use sensitive_url::SensitiveUrl;
|
||||
@@ -32,7 +31,6 @@ use std::collections::{HashMap, hash_map::Entry};
|
||||
use std::fmt;
|
||||
use std::future::Future;
|
||||
use std::io::Write;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
|
||||
@@ -45,6 +43,7 @@ use tokio::{
|
||||
use tokio_stream::wrappers::WatchStream;
|
||||
use tracing::{Instrument, debug, debug_span, error, info, instrument, warn};
|
||||
use tree_hash::TreeHash;
|
||||
use types::ExecutionPayloadGloas;
|
||||
use types::builder::BuilderBid;
|
||||
use types::execution::BlockProductionVersion;
|
||||
use types::kzg_ext::KzgCommitments;
|
||||
@@ -57,7 +56,6 @@ use types::{
|
||||
ExecutionPayloadCapella, ExecutionPayloadElectra, ExecutionPayloadFulu, FullPayload,
|
||||
ProposerPreparationData, Slot,
|
||||
};
|
||||
use types::{ExecutionPayloadGloas, new_non_zero_usize};
|
||||
|
||||
mod block_hash;
|
||||
mod engine_api;
|
||||
@@ -75,10 +73,6 @@ pub const DEFAULT_EXECUTION_ENDPOINT: &str = "http://localhost:8551/";
|
||||
/// Name for the default file used for the jwt secret.
|
||||
pub const DEFAULT_JWT_FILE: &str = "jwt.hex";
|
||||
|
||||
/// Each time the `ExecutionLayer` retrieves a block from an execution node, it stores that block
|
||||
/// in an LRU cache to avoid redundant lookups. This is the size of that cache.
|
||||
const EXECUTION_BLOCKS_LRU_CACHE_SIZE: NonZeroUsize = new_non_zero_usize(128);
|
||||
|
||||
/// A fee recipient address for use during block production. Only used as a very last resort if
|
||||
/// there is no address provided by the user.
|
||||
///
|
||||
@@ -452,7 +446,6 @@ struct Inner<E: EthSpec> {
|
||||
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,
|
||||
payload_cache: PayloadCache<E>,
|
||||
@@ -563,7 +556,6 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
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,
|
||||
payload_cache: PayloadCache::default(),
|
||||
last_new_payload_errored: RwLock::new(false),
|
||||
@@ -655,12 +647,6 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
.ok_or(ApiError::ExecutionHeadBlockNotFound)?;
|
||||
Ok(block.total_difficulty)
|
||||
}
|
||||
/// Note: this function returns a mutex guard, be careful to avoid deadlocks.
|
||||
async fn execution_blocks(
|
||||
&self,
|
||||
) -> MutexGuard<'_, LruCache<ExecutionBlockHash, ExecutionBlock>> {
|
||||
self.inner.execution_blocks.lock().await
|
||||
}
|
||||
|
||||
/// Gives access to a channel containing if the last engine state is online or not.
|
||||
///
|
||||
@@ -1641,208 +1627,6 @@ impl<E: EthSpec> ExecutionLayer<E> {
|
||||
Ok(versions)
|
||||
}
|
||||
|
||||
/// Used during block production to determine if the merge has been triggered.
|
||||
///
|
||||
/// ## Specification
|
||||
///
|
||||
/// `get_terminal_pow_block_hash`
|
||||
///
|
||||
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md
|
||||
pub async fn get_terminal_pow_block_hash(
|
||||
&self,
|
||||
spec: &ChainSpec,
|
||||
timestamp: u64,
|
||||
) -> Result<Option<ExecutionBlockHash>, Error> {
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::GET_TERMINAL_POW_BLOCK_HASH],
|
||||
);
|
||||
|
||||
let hash_opt = self
|
||||
.engine()
|
||||
.request(|engine| async move {
|
||||
let terminal_block_hash = spec.terminal_block_hash;
|
||||
if terminal_block_hash != ExecutionBlockHash::zero() {
|
||||
if self
|
||||
.get_pow_block(engine, terminal_block_hash)
|
||||
.await?
|
||||
.is_some()
|
||||
{
|
||||
return Ok(Some(terminal_block_hash));
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
|
||||
let block = self.get_pow_block_at_total_difficulty(engine, spec).await?;
|
||||
if let Some(pow_block) = block {
|
||||
// If `terminal_block.timestamp == transition_block.timestamp`,
|
||||
// we violate the invariant that a block's timestamp must be
|
||||
// strictly greater than its parent's timestamp.
|
||||
// The execution layer will reject a fcu call with such payload
|
||||
// attributes leading to a missed block.
|
||||
// Hence, we return `None` in such a case.
|
||||
if pow_block.timestamp >= timestamp {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
Ok(block.map(|b| b.block_hash))
|
||||
})
|
||||
.await
|
||||
.map_err(Box::new)
|
||||
.map_err(Error::EngineError)?;
|
||||
|
||||
if let Some(hash) = &hash_opt {
|
||||
info!(
|
||||
terminal_block_hash_override = ?spec.terminal_block_hash,
|
||||
terminal_total_difficulty = ?spec.terminal_total_difficulty,
|
||||
block_hash = ?hash,
|
||||
"Found terminal block hash"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(hash_opt)
|
||||
}
|
||||
|
||||
/// This function should remain internal. External users should use
|
||||
/// `self.get_terminal_pow_block` instead, since it checks against the terminal block hash
|
||||
/// override.
|
||||
///
|
||||
/// ## Specification
|
||||
///
|
||||
/// `get_pow_block_at_terminal_total_difficulty`
|
||||
///
|
||||
/// https://github.com/ethereum/consensus-specs/blob/v1.1.5/specs/merge/validator.md
|
||||
async fn get_pow_block_at_total_difficulty(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Option<ExecutionBlock>, ApiError> {
|
||||
let mut block = engine
|
||||
.api
|
||||
.get_block_by_number(BlockByNumberQuery::Tag(LATEST_TAG))
|
||||
.await?
|
||||
.ok_or(ApiError::ExecutionHeadBlockNotFound)?;
|
||||
|
||||
self.execution_blocks().await.put(block.block_hash, block);
|
||||
|
||||
loop {
|
||||
let block_reached_ttd =
|
||||
block.terminal_total_difficulty_reached(spec.terminal_total_difficulty);
|
||||
if block_reached_ttd {
|
||||
if block.parent_hash == ExecutionBlockHash::zero() {
|
||||
return Ok(Some(block));
|
||||
}
|
||||
let parent = self
|
||||
.get_pow_block(engine, block.parent_hash)
|
||||
.await?
|
||||
.ok_or(ApiError::ExecutionBlockNotFound(block.parent_hash))?;
|
||||
let parent_reached_ttd =
|
||||
parent.terminal_total_difficulty_reached(spec.terminal_total_difficulty);
|
||||
|
||||
if block_reached_ttd && !parent_reached_ttd {
|
||||
return Ok(Some(block));
|
||||
} else {
|
||||
block = parent;
|
||||
}
|
||||
} else {
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Used during block verification to check that a block correctly triggers the merge.
|
||||
///
|
||||
/// ## Returns
|
||||
///
|
||||
/// - `Some(true)` if the given `block_hash` is the terminal proof-of-work block.
|
||||
/// - `Some(false)` if the given `block_hash` is certainly *not* the terminal proof-of-work
|
||||
/// block.
|
||||
/// - `None` if the `block_hash` or its parent were not present on the execution engine.
|
||||
/// - `Err(_)` if there was an error connecting to the execution engine.
|
||||
///
|
||||
/// ## Fallback Behaviour
|
||||
///
|
||||
/// The request will be broadcast to all nodes, simultaneously. It will await a response (or
|
||||
/// failure) from all nodes and then return based on the first of these conditions which
|
||||
/// returns true:
|
||||
///
|
||||
/// - Terminal, if any node indicates it is terminal.
|
||||
/// - Not terminal, if any node indicates it is non-terminal.
|
||||
/// - Block not found, if any node cannot find the block.
|
||||
/// - An error, if all nodes return an error.
|
||||
///
|
||||
/// ## Specification
|
||||
///
|
||||
/// `is_valid_terminal_pow_block`
|
||||
///
|
||||
/// https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/merge/fork-choice.md
|
||||
pub async fn is_valid_terminal_pow_block_hash(
|
||||
&self,
|
||||
block_hash: ExecutionBlockHash,
|
||||
spec: &ChainSpec,
|
||||
) -> Result<Option<bool>, Error> {
|
||||
let _timer = metrics::start_timer_vec(
|
||||
&metrics::EXECUTION_LAYER_REQUEST_TIMES,
|
||||
&[metrics::IS_VALID_TERMINAL_POW_BLOCK_HASH],
|
||||
);
|
||||
|
||||
self.engine()
|
||||
.request(|engine| async move {
|
||||
if let Some(pow_block) = self.get_pow_block(engine, block_hash).await?
|
||||
&& let Some(pow_parent) =
|
||||
self.get_pow_block(engine, pow_block.parent_hash).await?
|
||||
{
|
||||
return Ok(Some(
|
||||
self.is_valid_terminal_pow_block(pow_block, pow_parent, spec),
|
||||
));
|
||||
}
|
||||
Ok(None)
|
||||
})
|
||||
.await
|
||||
.map_err(Box::new)
|
||||
.map_err(Error::EngineError)
|
||||
}
|
||||
|
||||
/// This function should remain internal.
|
||||
///
|
||||
/// External users should use `self.is_valid_terminal_pow_block_hash`.
|
||||
fn is_valid_terminal_pow_block(
|
||||
&self,
|
||||
block: ExecutionBlock,
|
||||
parent: ExecutionBlock,
|
||||
spec: &ChainSpec,
|
||||
) -> bool {
|
||||
let is_total_difficulty_reached =
|
||||
block.terminal_total_difficulty_reached(spec.terminal_total_difficulty);
|
||||
let is_parent_total_difficulty_valid = parent
|
||||
.total_difficulty
|
||||
.is_some_and(|td| td < spec.terminal_total_difficulty);
|
||||
is_total_difficulty_reached && is_parent_total_difficulty_valid
|
||||
}
|
||||
|
||||
/// Maps to the `eth_getBlockByHash` JSON-RPC call.
|
||||
async fn get_pow_block(
|
||||
&self,
|
||||
engine: &Engine,
|
||||
hash: ExecutionBlockHash,
|
||||
) -> Result<Option<ExecutionBlock>, ApiError> {
|
||||
if let Some(cached) = self.execution_blocks().await.get(&hash).copied() {
|
||||
// The block was in the cache, no need to request it from the execution
|
||||
// engine.
|
||||
return Ok(Some(cached));
|
||||
}
|
||||
|
||||
// The block was *not* in the cache, request it from the execution
|
||||
// engine and cache it for future reference.
|
||||
if let Some(block) = engine.api.get_block_by_hash(hash).await? {
|
||||
self.execution_blocks().await.put(hash, block);
|
||||
Ok(Some(block))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_payload_bodies_by_hash(
|
||||
&self,
|
||||
hashes: Vec<ExecutionBlockHash>,
|
||||
@@ -2330,15 +2114,6 @@ async fn timed_future<F: Future<Output = T>, T>(metric: &str, future: F) -> (T,
|
||||
(result, duration)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
/// Returns the duration since the unix epoch.
|
||||
fn timestamp_now() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap_or_else(|_| Duration::from_secs(0))
|
||||
.as_secs()
|
||||
}
|
||||
|
||||
fn noop<E: EthSpec>(
|
||||
_: &ExecutionLayer<E>,
|
||||
_: PayloadContentsRefTuple<E>,
|
||||
@@ -2359,7 +2134,6 @@ mod test {
|
||||
async fn produce_three_valid_pos_execution_blocks() {
|
||||
let runtime = TestRuntime::default();
|
||||
MockExecutionLayer::default_params(runtime.task_executor.clone())
|
||||
.move_to_terminal_block()
|
||||
.produce_valid_execution_payload_on_head()
|
||||
.await
|
||||
.produce_valid_execution_payload_on_head()
|
||||
@@ -2388,129 +2162,4 @@ mod test {
|
||||
Some(30_029_266)
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_forked_terminal_block() {
|
||||
let runtime = TestRuntime::default();
|
||||
let (mock, block_hash) = MockExecutionLayer::default_params(runtime.task_executor.clone())
|
||||
.move_to_terminal_block()
|
||||
.produce_forked_pow_block();
|
||||
assert!(
|
||||
mock.el
|
||||
.is_valid_terminal_pow_block_hash(block_hash, &mock.spec)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn finds_valid_terminal_block_hash() {
|
||||
let runtime = TestRuntime::default();
|
||||
MockExecutionLayer::default_params(runtime.task_executor.clone())
|
||||
.move_to_block_prior_to_terminal_block()
|
||||
.with_terminal_block(|spec, el, _| async move {
|
||||
el.engine().upcheck().await;
|
||||
assert_eq!(
|
||||
el.get_terminal_pow_block_hash(&spec, timestamp_now())
|
||||
.await
|
||||
.unwrap(),
|
||||
None
|
||||
)
|
||||
})
|
||||
.await
|
||||
.move_to_terminal_block()
|
||||
.with_terminal_block(|spec, el, terminal_block| async move {
|
||||
assert_eq!(
|
||||
el.get_terminal_pow_block_hash(&spec, timestamp_now())
|
||||
.await
|
||||
.unwrap(),
|
||||
Some(terminal_block.unwrap().block_hash)
|
||||
)
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_terminal_block_with_equal_timestamp() {
|
||||
let runtime = TestRuntime::default();
|
||||
MockExecutionLayer::default_params(runtime.task_executor.clone())
|
||||
.move_to_block_prior_to_terminal_block()
|
||||
.with_terminal_block(|spec, el, _| async move {
|
||||
el.engine().upcheck().await;
|
||||
assert_eq!(
|
||||
el.get_terminal_pow_block_hash(&spec, timestamp_now())
|
||||
.await
|
||||
.unwrap(),
|
||||
None
|
||||
)
|
||||
})
|
||||
.await
|
||||
.move_to_terminal_block()
|
||||
.with_terminal_block(|spec, el, terminal_block| async move {
|
||||
let timestamp = terminal_block.as_ref().map(|b| b.timestamp).unwrap();
|
||||
assert_eq!(
|
||||
el.get_terminal_pow_block_hash(&spec, timestamp)
|
||||
.await
|
||||
.unwrap(),
|
||||
None
|
||||
)
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn verifies_valid_terminal_block_hash() {
|
||||
let runtime = TestRuntime::default();
|
||||
MockExecutionLayer::default_params(runtime.task_executor.clone())
|
||||
.move_to_terminal_block()
|
||||
.with_terminal_block(|spec, el, terminal_block| async move {
|
||||
el.engine().upcheck().await;
|
||||
assert_eq!(
|
||||
el.is_valid_terminal_pow_block_hash(terminal_block.unwrap().block_hash, &spec)
|
||||
.await
|
||||
.unwrap(),
|
||||
Some(true)
|
||||
)
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_invalid_terminal_block_hash() {
|
||||
let runtime = TestRuntime::default();
|
||||
MockExecutionLayer::default_params(runtime.task_executor.clone())
|
||||
.move_to_terminal_block()
|
||||
.with_terminal_block(|spec, el, terminal_block| async move {
|
||||
el.engine().upcheck().await;
|
||||
let invalid_terminal_block = terminal_block.unwrap().parent_hash;
|
||||
|
||||
assert_eq!(
|
||||
el.is_valid_terminal_pow_block_hash(invalid_terminal_block, &spec)
|
||||
.await
|
||||
.unwrap(),
|
||||
Some(false)
|
||||
)
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn rejects_unknown_terminal_block_hash() {
|
||||
let runtime = TestRuntime::default();
|
||||
MockExecutionLayer::default_params(runtime.task_executor.clone())
|
||||
.move_to_terminal_block()
|
||||
.with_terminal_block(|spec, el, _| async move {
|
||||
el.engine().upcheck().await;
|
||||
let missing_terminal_block = ExecutionBlockHash::repeat_byte(42);
|
||||
|
||||
assert_eq!(
|
||||
el.is_valid_terminal_pow_block_hash(missing_terminal_block, &spec)
|
||||
.await
|
||||
.unwrap(),
|
||||
None
|
||||
)
|
||||
})
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,8 +10,6 @@ pub const GET_BLINDED_PAYLOAD_BUILDER: &str = "get_blinded_payload_builder";
|
||||
pub const POST_BLINDED_PAYLOAD_BUILDER: &str = "post_blinded_payload_builder";
|
||||
pub const NEW_PAYLOAD: &str = "new_payload";
|
||||
pub const FORKCHOICE_UPDATED: &str = "forkchoice_updated";
|
||||
pub const GET_TERMINAL_POW_BLOCK_HASH: &str = "get_terminal_pow_block_hash";
|
||||
pub const IS_VALID_TERMINAL_POW_BLOCK_HASH: &str = "is_valid_terminal_pow_block_hash";
|
||||
pub const LOCAL: &str = "local";
|
||||
pub const BUILDER: &str = "builder";
|
||||
pub const SUCCESS: &str = "success";
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
use crate::engine_api::{
|
||||
ExecutionBlock, PayloadAttributes, PayloadId, PayloadStatusV1, PayloadStatusV1Status,
|
||||
json_structures::{
|
||||
JsonForkchoiceUpdatedV1Response, JsonPayloadStatusV1, JsonPayloadStatusV1Status,
|
||||
BlobAndProof, BlobAndProofV1, BlobAndProofV2, JsonForkchoiceUpdatedV1Response,
|
||||
JsonPayloadStatusV1, JsonPayloadStatusV1Status,
|
||||
},
|
||||
};
|
||||
use crate::engines::ForkchoiceState;
|
||||
@@ -15,6 +16,7 @@ use rand::{Rng, SeedableRng, rngs::StdRng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use ssz::Decode;
|
||||
use ssz_types::VariableList;
|
||||
use state_processing::per_block_processing::deneb::kzg_commitment_to_versioned_hash;
|
||||
use std::cmp::max;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
@@ -28,8 +30,6 @@ use types::{
|
||||
Transactions, Uint256,
|
||||
};
|
||||
|
||||
use super::DEFAULT_TERMINAL_BLOCK;
|
||||
|
||||
const TEST_BLOB_BUNDLE: &[u8] = include_bytes!("fixtures/mainnet/test_blobs_bundle.ssz");
|
||||
const TEST_BLOB_BUNDLE_V2: &[u8] = include_bytes!("fixtures/mainnet/test_blobs_bundle_v2.ssz");
|
||||
|
||||
@@ -172,9 +172,6 @@ fn make_rng() -> Arc<Mutex<StdRng>> {
|
||||
impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
terminal_total_difficulty: Uint256,
|
||||
terminal_block_number: u64,
|
||||
terminal_block_hash: ExecutionBlockHash,
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
prague_time: Option<u64>,
|
||||
@@ -187,9 +184,9 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
finalized_block_hash: <_>::default(),
|
||||
blocks: <_>::default(),
|
||||
block_hashes: <_>::default(),
|
||||
terminal_total_difficulty,
|
||||
terminal_block_number,
|
||||
terminal_block_hash,
|
||||
terminal_total_difficulty: Default::default(),
|
||||
terminal_block_number: 0,
|
||||
terminal_block_hash: Default::default(),
|
||||
pending_payloads: <_>::default(),
|
||||
next_payload_id: 0,
|
||||
payload_ids: <_>::default(),
|
||||
@@ -293,25 +290,6 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
.and_then(|block| block.as_execution_payload())
|
||||
}
|
||||
|
||||
pub fn move_to_block_prior_to_terminal_block(&mut self) -> Result<(), String> {
|
||||
let target_block = self
|
||||
.terminal_block_number
|
||||
.checked_sub(1)
|
||||
.ok_or("terminal pow block is 0")?;
|
||||
self.move_to_pow_block(target_block)
|
||||
}
|
||||
|
||||
pub fn move_to_terminal_block(&mut self) -> Result<(), String> {
|
||||
self.move_to_pow_block(self.terminal_block_number)
|
||||
}
|
||||
|
||||
pub fn move_to_pow_block(&mut self, target_block: u64) -> Result<(), String> {
|
||||
let next_block = self.latest_block().unwrap().block_number() + 1;
|
||||
assert!(target_block >= next_block);
|
||||
|
||||
self.insert_pow_blocks(next_block..=target_block)
|
||||
}
|
||||
|
||||
pub fn drop_all_blocks(&mut self) {
|
||||
self.blocks = <_>::default();
|
||||
self.block_hashes = <_>::default();
|
||||
@@ -480,6 +458,40 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
|
||||
self.blobs_bundles.get(id).cloned()
|
||||
}
|
||||
|
||||
/// Look up a blob and proof by versioned hash across all stored bundles.
|
||||
pub fn get_blob_and_proof(&self, versioned_hash: &Hash256) -> Option<BlobAndProof<E>> {
|
||||
self.blobs_bundles
|
||||
.iter()
|
||||
.find_map(|(payload_id, blobs_bundle)| {
|
||||
let (blob_idx, _) =
|
||||
blobs_bundle
|
||||
.commitments
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find(|(_, commitment)| {
|
||||
&kzg_commitment_to_versioned_hash(commitment) == versioned_hash
|
||||
})?;
|
||||
let is_fulu = self.payload_ids.get(payload_id)?.fork_name().fulu_enabled();
|
||||
let blob = blobs_bundle.blobs.get(blob_idx)?.clone();
|
||||
if is_fulu {
|
||||
let start = blob_idx * E::cells_per_ext_blob();
|
||||
let end = start + E::cells_per_ext_blob();
|
||||
let proofs = blobs_bundle
|
||||
.proofs
|
||||
.get(start..end)?
|
||||
.to_vec()
|
||||
.try_into()
|
||||
.ok()?;
|
||||
Some(BlobAndProof::V2(BlobAndProofV2 { blob, proofs }))
|
||||
} else {
|
||||
Some(BlobAndProof::V1(BlobAndProofV1 {
|
||||
blob,
|
||||
proof: *blobs_bundle.proofs.get(blob_idx)?,
|
||||
}))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_payload(&mut self, payload: ExecutionPayload<E>) -> PayloadStatusV1 {
|
||||
let Some(parent) = self.blocks.get(&payload.parent_hash()) else {
|
||||
return PayloadStatusV1 {
|
||||
@@ -879,27 +891,22 @@ fn payload_id_from_u64(n: u64) -> PayloadId {
|
||||
n.to_le_bytes()
|
||||
}
|
||||
|
||||
pub fn generate_genesis_header<E: EthSpec>(
|
||||
spec: &ChainSpec,
|
||||
post_transition_merge: bool,
|
||||
) -> Option<ExecutionPayloadHeader<E>> {
|
||||
pub fn generate_genesis_header<E: EthSpec>(spec: &ChainSpec) -> Option<ExecutionPayloadHeader<E>> {
|
||||
let genesis_fork = spec.fork_name_at_slot::<E>(spec.genesis_slot);
|
||||
let genesis_block_hash =
|
||||
generate_genesis_block(spec.terminal_total_difficulty, DEFAULT_TERMINAL_BLOCK)
|
||||
.ok()
|
||||
.map(|block| block.block_hash);
|
||||
let genesis_block_hash = generate_genesis_block(Default::default(), 0)
|
||||
.ok()
|
||||
.map(|block| block.block_hash);
|
||||
let empty_transactions_root = Transactions::<E>::empty().tree_hash_root();
|
||||
match genesis_fork {
|
||||
ForkName::Base | ForkName::Altair => None,
|
||||
ForkName::Base | ForkName::Altair => {
|
||||
// Pre-Bellatrix forks have no execution payload
|
||||
None
|
||||
}
|
||||
ForkName::Bellatrix => {
|
||||
if post_transition_merge {
|
||||
let mut header = ExecutionPayloadHeader::Bellatrix(<_>::default());
|
||||
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
|
||||
*header.transactions_root_mut() = empty_transactions_root;
|
||||
Some(header)
|
||||
} else {
|
||||
Some(ExecutionPayloadHeader::<E>::Bellatrix(<_>::default()))
|
||||
}
|
||||
let mut header = ExecutionPayloadHeader::Bellatrix(<_>::default());
|
||||
*header.block_hash_mut() = genesis_block_hash.unwrap_or_default();
|
||||
*header.transactions_root_mut() = empty_transactions_root;
|
||||
Some(header)
|
||||
}
|
||||
ForkName::Capella => {
|
||||
let mut header = ExecutionPayloadHeader::Capella(<_>::default());
|
||||
@@ -985,70 +992,6 @@ mod test {
|
||||
use kzg::{Bytes48, CellRef, KzgBlobRef, trusted_setup::get_trusted_setup};
|
||||
use types::{MainnetEthSpec, MinimalEthSpec};
|
||||
|
||||
#[test]
|
||||
fn pow_chain_only() {
|
||||
const TERMINAL_DIFFICULTY: u64 = 10;
|
||||
const TERMINAL_BLOCK: u64 = 10;
|
||||
const DIFFICULTY_INCREMENT: u64 = 1;
|
||||
|
||||
let mut generator: ExecutionBlockGenerator<MainnetEthSpec> = ExecutionBlockGenerator::new(
|
||||
Uint256::from(TERMINAL_DIFFICULTY),
|
||||
TERMINAL_BLOCK,
|
||||
ExecutionBlockHash::zero(),
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
for i in 0..=TERMINAL_BLOCK {
|
||||
if i > 0 {
|
||||
generator.insert_pow_block(i).unwrap();
|
||||
}
|
||||
|
||||
/*
|
||||
* Generate a block, inspect it.
|
||||
*/
|
||||
|
||||
let block = generator.latest_block().unwrap();
|
||||
assert_eq!(block.block_number(), i);
|
||||
|
||||
let expected_parent = i
|
||||
.checked_sub(1)
|
||||
.map(|i| generator.block_by_number(i).unwrap().block_hash())
|
||||
.unwrap_or_else(ExecutionBlockHash::zero);
|
||||
assert_eq!(block.parent_hash(), expected_parent);
|
||||
|
||||
assert_eq!(
|
||||
block.total_difficulty().unwrap(),
|
||||
Uint256::from(i * DIFFICULTY_INCREMENT)
|
||||
);
|
||||
|
||||
assert_eq!(generator.block_by_hash(block.block_hash()).unwrap(), block);
|
||||
assert_eq!(generator.block_by_number(i).unwrap(), block);
|
||||
|
||||
/*
|
||||
* Check the parent is accessible.
|
||||
*/
|
||||
|
||||
if let Some(prev_i) = i.checked_sub(1) {
|
||||
assert_eq!(
|
||||
generator.block_by_number(prev_i).unwrap(),
|
||||
generator.block_by_hash(block.parent_hash()).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check the next block is inaccessible.
|
||||
*/
|
||||
|
||||
let next_i = i + 1;
|
||||
assert!(generator.block_by_number(next_i).is_none());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn valid_test_blobs_bundle_v1() {
|
||||
assert!(
|
||||
|
||||
@@ -476,6 +476,35 @@ pub async fn handle_rpc<E: EthSpec>(
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
ENGINE_GET_BLOBS_V1 => {
|
||||
let versioned_hashes =
|
||||
get_param::<Vec<Hash256>>(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
|
||||
let generator = ctx.execution_block_generator.read();
|
||||
// V1: per-element nullable array, positionally matching the request.
|
||||
let response: Vec<Option<BlobAndProofV1<E>>> = versioned_hashes
|
||||
.iter()
|
||||
.map(|hash| match generator.get_blob_and_proof(hash) {
|
||||
Some(BlobAndProof::V1(v1)) => Some(v1),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
Ok(serde_json::to_value(response).unwrap())
|
||||
}
|
||||
ENGINE_GET_BLOBS_V2 => {
|
||||
let versioned_hashes =
|
||||
get_param::<Vec<Hash256>>(params, 0).map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
|
||||
let generator = ctx.execution_block_generator.read();
|
||||
// V2: all-or-nothing — null if any blob is missing.
|
||||
let results: Vec<Option<BlobAndProofV2<E>>> = versioned_hashes
|
||||
.iter()
|
||||
.map(|hash| match generator.get_blob_and_proof(hash) {
|
||||
Some(BlobAndProof::V2(v2)) => Some(v2),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
let response: Option<Vec<BlobAndProofV2<E>>> = results.into_iter().collect();
|
||||
Ok(serde_json::to_value(response).unwrap())
|
||||
}
|
||||
ENGINE_FORKCHOICE_UPDATED_V1
|
||||
| ENGINE_FORKCHOICE_UPDATED_V2
|
||||
| ENGINE_FORKCHOICE_UPDATED_V3 => {
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
use crate::{
|
||||
test_utils::{
|
||||
DEFAULT_JWT_SECRET, DEFAULT_TERMINAL_BLOCK, DEFAULT_TERMINAL_DIFFICULTY, MockServer,
|
||||
},
|
||||
*,
|
||||
};
|
||||
use crate::{test_utils::DEFAULT_JWT_SECRET, test_utils::MockServer, *};
|
||||
use alloy_primitives::B256 as H256;
|
||||
use fixed_bytes::FixedBytesExtended;
|
||||
use kzg::Kzg;
|
||||
@@ -20,12 +15,10 @@ pub struct MockExecutionLayer<E: EthSpec> {
|
||||
impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
pub fn default_params(executor: TaskExecutor) -> Self {
|
||||
let mut spec = MainnetEthSpec::default_spec();
|
||||
spec.terminal_total_difficulty = Uint256::from(DEFAULT_TERMINAL_DIFFICULTY);
|
||||
spec.terminal_block_hash = ExecutionBlockHash::zero();
|
||||
spec.terminal_block_hash_activation_epoch = Epoch::new(0);
|
||||
Self::new(
|
||||
executor,
|
||||
DEFAULT_TERMINAL_BLOCK,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
@@ -40,7 +33,6 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
executor: TaskExecutor,
|
||||
terminal_block: u64,
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
prague_time: Option<u64>,
|
||||
@@ -56,9 +48,6 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
let server = MockServer::new(
|
||||
&handle,
|
||||
jwt_key,
|
||||
spec.terminal_total_difficulty,
|
||||
terminal_block,
|
||||
spec.terminal_block_hash,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
@@ -293,53 +282,4 @@ impl<E: EthSpec> MockExecutionLayer<E> {
|
||||
assert_eq!(head_execution_block.block_hash(), block_hash);
|
||||
assert_eq!(head_execution_block.parent_hash(), parent_hash);
|
||||
}
|
||||
|
||||
pub fn move_to_block_prior_to_terminal_block(self) -> Self {
|
||||
self.server
|
||||
.execution_block_generator()
|
||||
.move_to_block_prior_to_terminal_block()
|
||||
.unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn move_to_terminal_block(self) -> Self {
|
||||
self.server
|
||||
.execution_block_generator()
|
||||
.move_to_terminal_block()
|
||||
.unwrap();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn produce_forked_pow_block(self) -> (Self, ExecutionBlockHash) {
|
||||
let head_block = self
|
||||
.server
|
||||
.execution_block_generator()
|
||||
.latest_block()
|
||||
.unwrap();
|
||||
|
||||
let block_hash = self
|
||||
.server
|
||||
.execution_block_generator()
|
||||
.insert_pow_block_by_hash(head_block.parent_hash(), 1)
|
||||
.unwrap();
|
||||
(self, block_hash)
|
||||
}
|
||||
|
||||
pub async fn with_terminal_block<U, V>(self, func: U) -> Self
|
||||
where
|
||||
U: Fn(Arc<ChainSpec>, ExecutionLayer<E>, Option<ExecutionBlock>) -> V,
|
||||
V: Future<Output = ()>,
|
||||
{
|
||||
let terminal_block_number = self
|
||||
.server
|
||||
.execution_block_generator()
|
||||
.terminal_block_number;
|
||||
let terminal_block = self
|
||||
.server
|
||||
.execution_block_generator()
|
||||
.execution_block_by_number(terminal_block_number);
|
||||
|
||||
func(self.spec.clone(), self.el.clone(), terminal_block).await;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,6 @@ pub use hook::Hook;
|
||||
pub use mock_builder::{MockBuilder, Operation, mock_builder_extra_data};
|
||||
pub use mock_execution_layer::MockExecutionLayer;
|
||||
|
||||
pub const DEFAULT_TERMINAL_DIFFICULTY: u64 = 6400;
|
||||
pub const DEFAULT_TERMINAL_BLOCK: u64 = 64;
|
||||
pub const DEFAULT_JWT_SECRET: [u8; 32] = [42; 32];
|
||||
pub const DEFAULT_MOCK_EL_PAYLOAD_VALUE_WEI: u128 = 10_000_000_000_000_000;
|
||||
pub const DEFAULT_BUILDER_PAYLOAD_VALUE_WEI: u128 = 20_000_000_000_000_000;
|
||||
@@ -80,9 +78,6 @@ mod mock_execution_layer;
|
||||
pub struct MockExecutionConfig {
|
||||
pub server_config: Config,
|
||||
pub jwt_key: JwtKey,
|
||||
pub terminal_difficulty: Uint256,
|
||||
pub terminal_block: u64,
|
||||
pub terminal_block_hash: ExecutionBlockHash,
|
||||
pub shanghai_time: Option<u64>,
|
||||
pub cancun_time: Option<u64>,
|
||||
pub prague_time: Option<u64>,
|
||||
@@ -94,9 +89,6 @@ impl Default for MockExecutionConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
jwt_key: JwtKey::random(),
|
||||
terminal_difficulty: Uint256::from(DEFAULT_TERMINAL_DIFFICULTY),
|
||||
terminal_block: DEFAULT_TERMINAL_BLOCK,
|
||||
terminal_block_hash: ExecutionBlockHash::zero(),
|
||||
server_config: Config::default(),
|
||||
shanghai_time: None,
|
||||
cancun_time: None,
|
||||
@@ -119,9 +111,6 @@ impl<E: EthSpec> MockServer<E> {
|
||||
Self::new(
|
||||
&runtime::Handle::current(),
|
||||
JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap(),
|
||||
Uint256::from(DEFAULT_TERMINAL_DIFFICULTY),
|
||||
DEFAULT_TERMINAL_BLOCK,
|
||||
ExecutionBlockHash::zero(),
|
||||
None, // FIXME(capella): should this be the default?
|
||||
None, // FIXME(deneb): should this be the default?
|
||||
None, // FIXME(electra): should this be the default?
|
||||
@@ -139,9 +128,6 @@ impl<E: EthSpec> MockServer<E> {
|
||||
create_test_tracing_subscriber();
|
||||
let MockExecutionConfig {
|
||||
jwt_key,
|
||||
terminal_difficulty,
|
||||
terminal_block,
|
||||
terminal_block_hash,
|
||||
server_config,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
@@ -152,9 +138,6 @@ impl<E: EthSpec> MockServer<E> {
|
||||
let last_echo_request = Arc::new(RwLock::new(None));
|
||||
let preloaded_responses = Arc::new(Mutex::new(vec![]));
|
||||
let execution_block_generator = ExecutionBlockGenerator::new(
|
||||
terminal_difficulty,
|
||||
terminal_block,
|
||||
terminal_block_hash,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
@@ -216,9 +199,6 @@ impl<E: EthSpec> MockServer<E> {
|
||||
pub fn new(
|
||||
handle: &runtime::Handle,
|
||||
jwt_key: JwtKey,
|
||||
terminal_difficulty: Uint256,
|
||||
terminal_block: u64,
|
||||
terminal_block_hash: ExecutionBlockHash,
|
||||
shanghai_time: Option<u64>,
|
||||
cancun_time: Option<u64>,
|
||||
prague_time: Option<u64>,
|
||||
@@ -231,9 +211,6 @@ impl<E: EthSpec> MockServer<E> {
|
||||
MockExecutionConfig {
|
||||
server_config: Config::default(),
|
||||
jwt_key,
|
||||
terminal_difficulty,
|
||||
terminal_block,
|
||||
terminal_block_hash,
|
||||
shanghai_time,
|
||||
cancun_time,
|
||||
prague_time,
|
||||
|
||||
Reference in New Issue
Block a user