Merge unstable 20230925 into deneb-free-blobs.

This commit is contained in:
Jimmy Chen
2023-09-26 10:32:18 +10:00
164 changed files with 3843 additions and 3056 deletions
+57 -59
View File
@@ -2,7 +2,7 @@
name = "beacon_chain"
version = "0.2.0"
authors = ["Paul Hauner <paul@paulhauner.com>", "Age Manning <Age@AgeManning.com>"]
edition = "2021"
edition = { workspace = true }
autotests = false # using a single test binary compiles faster
[features]
@@ -12,66 +12,64 @@ participation_metrics = [] # Exposes validator participation metrics to Prometh
fork_from_env = [] # Initialise the harness chain spec from the FORK_NAME env variable
[dev-dependencies]
maplit = "1.0.2"
environment = { path = "../../lighthouse/environment" }
serde_json = "1.0.58"
maplit = { workspace = true }
environment = { workspace = true }
serde_json = { workspace = true }
[dependencies]
serde_json = "1.0.58"
eth2_network_config = { path = "../../common/eth2_network_config"}
merkle_proof = { path = "../../consensus/merkle_proof" }
store = { path = "../store" }
parking_lot = "0.12.0"
lazy_static = "1.4.0"
smallvec = "1.6.1"
lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
operation_pool = { path = "../operation_pool" }
rayon = "1.4.1"
serde = "1.0.116"
serde_derive = "1.0.116"
ethereum_serde_utils = "0.5.0"
slog = { version = "2.5.2", features = ["max_level_trace"] }
sloggers = { version = "2.1.1", features = ["json"] }
slot_clock = { path = "../../common/slot_clock" }
ethereum_hashing = "1.0.0-beta.2"
ethereum_ssz = "0.5.0"
ssz_types = "0.5.4"
ethereum_ssz_derive = "0.5.3"
state_processing = { path = "../../consensus/state_processing" }
tree_hash_derive = "0.5.0"
tree_hash = "0.5.2"
types = { path = "../../consensus/types" }
tokio = "1.14.0"
tokio-stream = "0.1.3"
eth1 = { path = "../eth1" }
futures = "0.3.7"
genesis = { path = "../genesis" }
int_to_bytes = { path = "../../consensus/int_to_bytes" }
rand = "0.8.5"
proto_array = { path = "../../consensus/proto_array" }
lru = "0.7.1"
tempfile = "3.1.0"
bitvec = "0.20.4"
bls = { path = "../../crypto/bls" }
kzg = { path = "../../crypto/kzg" }
safe_arith = { path = "../../consensus/safe_arith" }
fork_choice = { path = "../../consensus/fork_choice" }
task_executor = { path = "../../common/task_executor" }
derivative = "2.1.1"
itertools = "0.10.0"
slasher = { path = "../../slasher" }
eth2 = { path = "../../common/eth2" }
strum = { version = "0.24.0", features = ["derive"] }
logging = { path = "../../common/logging" }
execution_layer = { path = "../execution_layer" }
sensitive_url = { path = "../../common/sensitive_url" }
superstruct = "0.5.0"
hex = "0.4.2"
exit-future = "0.2.0"
unused_port = {path = "../../common/unused_port"}
oneshot_broadcast = { path = "../../common/oneshot_broadcast" }
slog-term = "2.6.0"
slog-async = "2.5.0"
serde_json = { workspace = true }
eth2_network_config = { workspace = true }
merkle_proof = { workspace = true }
store = { workspace = true }
parking_lot = { workspace = true }
lazy_static = { workspace = true }
smallvec = { workspace = true }
lighthouse_metrics = { workspace = true }
operation_pool = { workspace = true }
rayon = { workspace = true }
serde = { workspace = true }
ethereum_serde_utils = { workspace = true }
slog = { workspace = true }
sloggers = { workspace = true }
slot_clock = { workspace = true }
ethereum_hashing = { workspace = true }
ethereum_ssz = { workspace = true }
ssz_types = { workspace = true }
ethereum_ssz_derive = { workspace = true }
state_processing = { workspace = true }
tree_hash_derive = { workspace = true }
tree_hash = { workspace = true }
types = { workspace = true }
tokio = { workspace = true }
tokio-stream = { workspace = true }
eth1 = { workspace = true }
futures = { workspace = true }
genesis = { workspace = true }
int_to_bytes = { workspace = true }
rand = { workspace = true }
proto_array = { workspace = true }
lru = { workspace = true }
tempfile = { workspace = true }
bitvec = { workspace = true }
bls = { workspace = true }
kzg = { workspace = true }
safe_arith = { workspace = true }
fork_choice = { workspace = true }
task_executor = { workspace = true }
derivative = { workspace = true }
itertools = { workspace = true }
slasher = { workspace = true }
eth2 = { workspace = true }
strum = { workspace = true }
logging = { workspace = true }
execution_layer = { workspace = true }
sensitive_url = { workspace = true }
superstruct = { workspace = true }
hex = { workspace = true }
exit-future = { workspace = true }
oneshot_broadcast = { path = "../../common/oneshot_broadcast/" }
slog-term = { workspace = true }
slog-async = { workspace = true }
[[test]]
name = "beacon_chain_tests"
+9 -9
View File
@@ -874,10 +874,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
///
/// May return a database error.
pub fn state_root_at_slot(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> {
if request_slot > self.slot()? {
return Ok(None);
} else if request_slot == self.spec.genesis_slot {
if request_slot == self.spec.genesis_slot {
return Ok(Some(self.genesis_state_root));
} else if request_slot > self.slot()? {
return Ok(None);
}
// Check limits w.r.t historic state bounds.
@@ -954,10 +954,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
///
/// May return a database error.
fn block_root_at_slot_skips_none(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> {
if request_slot > self.slot()? {
return Ok(None);
} else if request_slot == self.spec.genesis_slot {
if request_slot == self.spec.genesis_slot {
return Ok(Some(self.genesis_block_root));
} else if request_slot > self.slot()? {
return Ok(None);
}
let prev_slot = request_slot.saturating_sub(1_u64);
@@ -1017,10 +1017,10 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
///
/// May return a database error.
fn block_root_at_slot_skips_prev(&self, request_slot: Slot) -> Result<Option<Hash256>, Error> {
if request_slot > self.slot()? {
return Ok(None);
} else if request_slot == self.spec.genesis_slot {
if request_slot == self.spec.genesis_slot {
return Ok(Some(self.genesis_block_root));
} else if request_slot > self.slot()? {
return Ok(None);
}
// Try an optimized path of reading the root directly from the head state.
@@ -1,4 +1,4 @@
use serde_derive::Serialize;
use serde::Serialize;
use std::sync::Arc;
use types::{
beacon_state::CloneConfig, AbstractExecPayload, BeaconState, EthSpec, FullPayload, Hash256,
@@ -811,11 +811,8 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
// reboot if the `observed_block_producers` cache is empty. In that case, without this
// check, we will load the parent and state from disk only to find out later that we
// already know this block.
if chain
.canonical_head
.fork_choice_read_lock()
.contains_block(&block_root)
{
let fork_choice_read_lock = chain.canonical_head.fork_choice_read_lock();
if fork_choice_read_lock.contains_block(&block_root) {
return Err(BlockError::BlockIsAlreadyKnown);
}
@@ -824,9 +821,10 @@ impl<T: BeaconChainTypes> GossipVerifiedBlock<T> {
// We check this *before* we load the parent so that we can return a more detailed error.
let block = check_block_is_finalized_checkpoint_or_descendant(
chain,
&chain.canonical_head.fork_choice_read_lock(),
&fork_choice_read_lock,
block,
)?;
drop(fork_choice_read_lock);
let block_epoch = block.slot().epoch(T::EthSpec::slots_per_epoch());
let (parent_block, block) = verify_parent_block_is_known(chain, block)?;
+1 -1
View File
@@ -1,5 +1,5 @@
pub use proto_array::{DisallowedReOrgOffsets, ReOrgThreshold};
use serde_derive::{Deserialize, Serialize};
use serde::{Deserialize, Serialize};
use std::time::Duration;
use types::{Checkpoint, Epoch, ProgressiveBalancesMode};
+2
View File
@@ -149,6 +149,8 @@ pub enum BeaconChainError {
BlockVariantLacksExecutionPayload(Hash256),
ExecutionLayerErrorPayloadReconstruction(ExecutionBlockHash, Box<execution_layer::Error>),
EngineGetCapabilititesFailed(Box<execution_layer::Error>),
ExecutionLayerGetBlockByNumberFailed(Box<execution_layer::Error>),
ExecutionLayerGetBlockByHashFailed(Box<execution_layer::Error>),
BlockHashMissingFromExecutionLayer(ExecutionBlockHash),
InconsistentPayloadReconstructed {
slot: Slot,
+109 -3
View File
@@ -1,8 +1,10 @@
//! Provides tools for checking if a node is ready for the Bellatrix upgrade and following merge
//! transition.
use crate::{BeaconChain, BeaconChainTypes};
use crate::{BeaconChain, BeaconChainError as Error, BeaconChainTypes};
use execution_layer::BlockByNumberQuery;
use serde::{Deserialize, Serialize, Serializer};
use slog::debug;
use std::fmt;
use std::fmt::Write;
use types::*;
@@ -120,6 +122,25 @@ impl fmt::Display for MergeReadiness {
}
}
pub enum GenesisExecutionPayloadStatus {
Correct(ExecutionBlockHash),
BlockHashMismatch {
got: ExecutionBlockHash,
expected: ExecutionBlockHash,
},
TransactionsRootMismatch {
got: Hash256,
expected: Hash256,
},
WithdrawalsRootMismatch {
got: Hash256,
expected: Hash256,
},
OtherMismatch,
Irrelevant,
AlreadyHappened,
}
impl<T: BeaconChainTypes> BeaconChain<T> {
/// Returns `true` if user has an EL configured, or if the Bellatrix fork has occurred or will
/// occur within `MERGE_READINESS_PREPARATION_SECONDS`.
@@ -144,9 +165,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
/// Attempts to connect to the EL and confirm that it is ready for the merge.
pub async fn check_merge_readiness(&self) -> MergeReadiness {
pub async fn check_merge_readiness(&self, current_slot: Slot) -> MergeReadiness {
if let Some(el) = self.execution_layer.as_ref() {
if !el.is_synced_for_notifier().await {
if !el.is_synced_for_notifier(current_slot).await {
// The EL is not synced.
return MergeReadiness::NotSynced;
}
@@ -161,6 +182,91 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
MergeReadiness::NoExecutionEndpoint
}
}
/// Check that the execution payload embedded in the genesis state matches the EL's genesis
/// block.
pub async fn check_genesis_execution_payload_is_correct(
&self,
) -> Result<GenesisExecutionPayloadStatus, Error> {
let head_snapshot = self.head_snapshot();
let genesis_state = &head_snapshot.beacon_state;
if genesis_state.slot() != 0 {
return Ok(GenesisExecutionPayloadStatus::AlreadyHappened);
}
let Ok(latest_execution_payload_header) = genesis_state.latest_execution_payload_header()
else {
return Ok(GenesisExecutionPayloadStatus::Irrelevant);
};
let fork = self.spec.fork_name_at_epoch(Epoch::new(0));
let execution_layer = self
.execution_layer
.as_ref()
.ok_or(Error::ExecutionLayerMissing)?;
let exec_block_hash = latest_execution_payload_header.block_hash();
// Use getBlockByNumber(0) to check that the block hash matches.
// At present, Geth does not respond to engine_getPayloadBodiesByRange before genesis.
let execution_block = execution_layer
.get_block_by_number(BlockByNumberQuery::Tag("0x0"))
.await
.map_err(|e| Error::ExecutionLayerGetBlockByNumberFailed(Box::new(e)))?
.ok_or(Error::BlockHashMissingFromExecutionLayer(exec_block_hash))?;
if execution_block.block_hash != exec_block_hash {
return Ok(GenesisExecutionPayloadStatus::BlockHashMismatch {
got: execution_block.block_hash,
expected: exec_block_hash,
});
}
// Double-check the block by reconstructing it.
let execution_payload = execution_layer
.get_payload_by_hash_legacy(exec_block_hash, fork)
.await
.map_err(|e| Error::ExecutionLayerGetBlockByHashFailed(Box::new(e)))?
.ok_or(Error::BlockHashMissingFromExecutionLayer(exec_block_hash))?;
// Verify payload integrity.
let header_from_payload = ExecutionPayloadHeader::from(execution_payload.to_ref());
let got_transactions_root = header_from_payload.transactions_root();
let expected_transactions_root = latest_execution_payload_header.transactions_root();
let got_withdrawals_root = header_from_payload.withdrawals_root().ok();
let expected_withdrawals_root = latest_execution_payload_header.withdrawals_root().ok();
if got_transactions_root != expected_transactions_root {
return Ok(GenesisExecutionPayloadStatus::TransactionsRootMismatch {
got: got_transactions_root,
expected: expected_transactions_root,
});
}
if let Some(&expected) = expected_withdrawals_root {
if let Some(&got) = got_withdrawals_root {
if got != expected {
return Ok(GenesisExecutionPayloadStatus::WithdrawalsRootMismatch {
got,
expected,
});
}
}
}
if header_from_payload.to_ref() != latest_execution_payload_header {
debug!(
self.log,
"Genesis execution payload reconstruction failure";
"consensus_node_header" => ?latest_execution_payload_header,
"execution_node_header" => ?header_from_payload
);
return Ok(GenesisExecutionPayloadStatus::OtherMismatch);
}
Ok(GenesisExecutionPayloadStatus::Correct(exec_block_hash))
}
}
/// Utility function to serialize a Uint256 as a decimal string.
+52 -42
View File
@@ -20,8 +20,8 @@ use execution_layer::test_utils::generate_genesis_header;
use execution_layer::{
auth::JwtKey,
test_utils::{
ExecutionBlockGenerator, MockExecutionLayer, TestingBuilder, DEFAULT_JWT_SECRET,
DEFAULT_TERMINAL_BLOCK,
ExecutionBlockGenerator, MockBuilder, MockBuilderServer, MockExecutionLayer,
DEFAULT_JWT_SECRET, DEFAULT_TERMINAL_BLOCK,
},
ExecutionLayer,
};
@@ -175,7 +175,6 @@ pub struct Builder<T: BeaconChainTypes> {
store_mutator: Option<BoxedMutator<T::EthSpec, T::HotStore, T::ColdStore>>,
execution_layer: Option<ExecutionLayer<T::EthSpec>>,
mock_execution_layer: Option<MockExecutionLayer<T::EthSpec>>,
mock_builder: Option<TestingBuilder<T::EthSpec>>,
testing_slot_clock: Option<TestingSlotClock>,
runtime: TestRuntime,
log: Logger,
@@ -311,7 +310,6 @@ where
store_mutator: None,
execution_layer: None,
mock_execution_layer: None,
mock_builder: None,
testing_slot_clock: None,
runtime,
log,
@@ -451,50 +449,21 @@ where
self
}
pub fn mock_execution_layer(mut self) -> Self {
pub fn mock_execution_layer(self) -> Self {
self.mock_execution_layer_with_config()
}
pub fn mock_execution_layer_with_config(mut self) -> Self {
let mock = mock_execution_layer_from_parts::<E>(
self.spec.as_ref().expect("cannot build without spec"),
self.runtime.task_executor.clone(),
None,
None,
);
self.execution_layer = Some(mock.el.clone());
self.mock_execution_layer = Some(mock);
self
}
pub fn mock_execution_layer_with_builder(
mut self,
beacon_url: SensitiveUrl,
builder_threshold: Option<u128>,
) -> Self {
// Get a random unused port
let port = unused_port::unused_tcp4_port().unwrap();
let builder_url = SensitiveUrl::parse(format!("http://127.0.0.1:{port}").as_str()).unwrap();
let spec = self.spec.as_ref().expect("cannot build without spec");
let mock_el = mock_execution_layer_from_parts::<E>(
spec,
self.runtime.task_executor.clone(),
Some(builder_url.clone()),
builder_threshold,
)
.move_to_terminal_block();
let mock_el_url = SensitiveUrl::parse(mock_el.server.url().as_str()).unwrap();
self.mock_builder = Some(TestingBuilder::new(
mock_el_url,
builder_url,
beacon_url,
spec.clone(),
self.runtime.task_executor.clone(),
));
self.execution_layer = Some(mock_el.el.clone());
self.mock_execution_layer = Some(mock_el);
self
}
/// Instruct the mock execution engine to always return a "valid" response to any payload it is
/// asked to execute.
pub fn mock_execution_layer_all_payloads_valid(self) -> Self {
@@ -581,7 +550,7 @@ where
shutdown_receiver: Arc::new(Mutex::new(shutdown_receiver)),
runtime: self.runtime,
mock_execution_layer: self.mock_execution_layer,
mock_builder: self.mock_builder.map(Arc::new),
mock_builder: None,
blob_signature_cache: <_>::default(),
rng: make_rng(),
}
@@ -591,7 +560,6 @@ where
pub fn mock_execution_layer_from_parts<T: EthSpec>(
spec: &ChainSpec,
task_executor: TaskExecutor,
builder_url: Option<SensitiveUrl>,
builder_threshold: Option<u128>,
) -> MockExecutionLayer<T> {
let shanghai_time = spec.capella_fork_epoch.map(|epoch| {
@@ -615,7 +583,6 @@ pub fn mock_execution_layer_from_parts<T: EthSpec>(
builder_threshold,
Some(JwtKey::from_slice(&DEFAULT_JWT_SECRET).unwrap()),
spec.clone(),
builder_url,
Some(kzg),
)
}
@@ -639,7 +606,7 @@ pub struct BeaconChainHarness<T: BeaconChainTypes> {
pub runtime: TestRuntime,
pub mock_execution_layer: Option<MockExecutionLayer<T::EthSpec>>,
pub mock_builder: Option<Arc<TestingBuilder<T::EthSpec>>>,
pub mock_builder: Option<Arc<MockBuilder<T::EthSpec>>>,
/// Cache for blob signature because we don't need them for import, but we do need them
/// to test gossip validation. We always make them during block production but drop them
@@ -695,6 +662,49 @@ where
.execution_block_generator()
}
pub fn set_mock_builder(&mut self, beacon_url: SensitiveUrl) -> MockBuilderServer {
let mock_el = self
.mock_execution_layer
.as_ref()
.expect("harness was not built with mock execution layer");
let mock_el_url = SensitiveUrl::parse(mock_el.server.url().as_str()).unwrap();
// Create the builder, listening on a free port.
let (mock_builder, mock_builder_server) = MockBuilder::new_for_testing(
mock_el_url,
beacon_url,
self.spec.clone(),
self.runtime.task_executor.clone(),
);
// Set the builder URL in the execution layer now that its port is known.
let builder_listen_addr = mock_builder_server.local_addr();
let port = builder_listen_addr.port();
mock_el
.el
.set_builder_url(
SensitiveUrl::parse(format!("http://127.0.0.1:{port}").as_str()).unwrap(),
None,
)
.unwrap();
self.mock_builder = Some(Arc::new(mock_builder));
// Sanity check.
let el_builder = self
.chain
.execution_layer
.as_ref()
.unwrap()
.builder()
.unwrap();
let mock_el_builder = mock_el.el.builder().unwrap();
assert!(Arc::ptr_eq(&el_builder, &mock_el_builder));
mock_builder_server
}
pub fn get_head_block(&self) -> RpcBlock<E> {
let block = self.chain.head_beacon_block();
let block_root = block.canonical_root();