Skip to content

Commit

Permalink
Misc changes for merge testnets (#2667)
Browse files Browse the repository at this point in the history
* Thread eth1_block_hash into interop genesis state

* Add merge-fork-epoch flag

* Build LH with minimal spec by default

* Add verbose logs to execution_layer

* Add --http-allow-sync-stalled flag

* Update lcli new-testnet to create genesis state

* Fix http test

* Fix compile errors in tests
  • Loading branch information
paulhauner authored Oct 2, 2021
1 parent 2c4aabd commit dc6aa25
Show file tree
Hide file tree
Showing 16 changed files with 237 additions and 61 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ PINNED_NIGHTLY ?= nightly
# Binaries will most likely be found in `./target/release`
install:
ifeq ($(PORTABLE), true)
cargo install --path lighthouse --force --locked --features portable
cargo install --path lighthouse --force --locked --features portable,spec-minimal
else
cargo install --path lighthouse --force --locked
cargo install --path lighthouse --force --locked --features spec-minimal
endif

# Builds the lcli binary in release (optimized).
Expand Down
14 changes: 11 additions & 3 deletions beacon_node/beacon_chain/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -893,7 +893,9 @@ fn descriptive_db_error(item: &str, error: &StoreError) -> String {
mod test {
use super::*;
use eth2_hashing::hash;
use genesis::{generate_deterministic_keypairs, interop_genesis_state};
use genesis::{
generate_deterministic_keypairs, interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH,
};
use sloggers::{null::NullLoggerBuilder, Build};
use ssz::Encode;
use std::time::Duration;
Expand Down Expand Up @@ -925,6 +927,7 @@ mod test {
let genesis_state = interop_genesis_state(
&generate_deterministic_keypairs(validator_count),
genesis_time,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
&spec,
)
.expect("should create interop genesis state");
Expand Down Expand Up @@ -990,8 +993,13 @@ mod test {

let keypairs = generate_deterministic_keypairs(validator_count);

let state = interop_genesis_state::<TestEthSpec>(&keypairs, genesis_time, spec)
.expect("should build state");
let state = interop_genesis_state::<TestEthSpec>(
&keypairs,
genesis_time,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
spec,
)
.expect("should build state");

assert_eq!(
state.eth1_data().block_hash,
Expand Down
4 changes: 3 additions & 1 deletion beacon_node/beacon_chain/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use execution_layer::{
ExecutionLayer,
};
use futures::channel::mpsc::Receiver;
pub use genesis::interop_genesis_state;
pub use genesis::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
use int_to_bytes::int_to_bytes32;
use merkle_proof::MerkleTree;
use parking_lot::Mutex;
Expand Down Expand Up @@ -203,6 +203,7 @@ impl<E: EthSpec> Builder<EphemeralHarnessType<E>> {
let genesis_state = interop_genesis_state::<E>(
&validator_keypairs,
HARNESS_GENESIS_TIME,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
builder.get_spec(),
)
.expect("should generate interop state");
Expand All @@ -227,6 +228,7 @@ impl<E: EthSpec> Builder<DiskHarnessType<E>> {
let genesis_state = interop_genesis_state::<E>(
&validator_keypairs,
HARNESS_GENESIS_TIME,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
builder.get_spec(),
)
.expect("should generate interop state");
Expand Down
12 changes: 9 additions & 3 deletions beacon_node/client/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use eth2::{
};
use eth2_libp2p::NetworkGlobals;
use execution_layer::ExecutionLayer;
use genesis::{interop_genesis_state, Eth1GenesisService};
use genesis::{interop_genesis_state, Eth1GenesisService, DEFAULT_ETH1_BLOCK_HASH};
use monitoring_api::{MonitoringHttpClient, ProcessType};
use network::{NetworkConfig, NetworkMessage, NetworkService};
use slasher::Slasher;
Expand All @@ -31,7 +31,8 @@ use std::time::Duration;
use timer::spawn_timer;
use tokio::sync::{mpsc::UnboundedSender, oneshot};
use types::{
test_utils::generate_deterministic_keypairs, BeaconState, ChainSpec, EthSpec, SignedBeaconBlock,
test_utils::generate_deterministic_keypairs, BeaconState, ChainSpec, EthSpec, Hash256,
SignedBeaconBlock,
};

/// Interval between polling the eth1 node for genesis information.
Expand Down Expand Up @@ -229,7 +230,12 @@ where
genesis_time,
} => {
let keypairs = generate_deterministic_keypairs(validator_count);
let genesis_state = interop_genesis_state(&keypairs, genesis_time, &spec)?;
let genesis_state = interop_genesis_state(
&keypairs,
genesis_time,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
&spec,
)?;
builder.genesis_state(genesis_state).map(|v| (v, None))?
}
ClientGenesis::SszBytes {
Expand Down
55 changes: 52 additions & 3 deletions beacon_node/execution_layer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use engine_api::{Error as ApiError, *};
use engines::{Engine, EngineError, Engines};
use lru::LruCache;
use sensitive_url::SensitiveUrl;
use slog::{crit, Logger};
use slog::{crit, info, Logger};
use std::future::Future;
use std::sync::Arc;
use task_executor::TaskExecutor;
Expand Down Expand Up @@ -177,6 +177,14 @@ impl ExecutionLayer {
random: Hash256,
) -> Result<PayloadId, Error> {
let fee_recipient = self.fee_recipient()?;
info!(
self.log(),
"Issuing engine_preparePayload";
"fee_recipient" => ?fee_recipient,
"random" => ?random,
"timestamp" => timestamp,
"parent_hash" => ?parent_hash,
);
self.engines()
.first_success(|engine| {
// TODO(merge): make a cache for these IDs, so we don't always have to perform this
Expand Down Expand Up @@ -205,6 +213,14 @@ impl ExecutionLayer {
random: Hash256,
) -> Result<ExecutionPayload<T>, Error> {
let fee_recipient = self.fee_recipient()?;
info!(
self.log(),
"Issuing engine_getPayload";
"fee_recipient" => ?fee_recipient,
"random" => ?random,
"timestamp" => timestamp,
"parent_hash" => ?parent_hash,
);
self.engines()
.first_success(|engine| async move {
// TODO(merge): make a cache for these IDs, so we don't always have to perform this
Expand Down Expand Up @@ -236,6 +252,14 @@ impl ExecutionLayer {
&self,
execution_payload: &ExecutionPayload<T>,
) -> Result<(ExecutePayloadResponse, ExecutePayloadHandle), Error> {
info!(
self.log(),
"Issuing engine_executePayload";
"parent_hash" => ?execution_payload.parent_hash,
"block_hash" => ?execution_payload.block_hash,
"block_number" => execution_payload.block_number,
);

let broadcast_results = self
.engines()
.broadcast(|engine| engine.api.execute_payload(execution_payload.clone()))
Expand Down Expand Up @@ -296,6 +320,12 @@ impl ExecutionLayer {
block_hash: Hash256,
status: ConsensusStatus,
) -> Result<(), Error> {
info!(
self.log(),
"Issuing engine_consensusValidated";
"status" => ?status,
"block_hash" => ?block_hash,
);
let broadcast_results = self
.engines()
.broadcast(|engine| engine.api.consensus_validated(block_hash, status))
Expand Down Expand Up @@ -328,6 +358,12 @@ impl ExecutionLayer {
head_block_hash: Hash256,
finalized_block_hash: Hash256,
) -> Result<(), Error> {
info!(
self.log(),
"Issuing engine_forkchoiceUpdated";
"finalized_block_hash" => ?finalized_block_hash,
"head_block_hash" => ?head_block_hash,
);
let broadcast_results = self
.engines()
.broadcast(|engine| {
Expand Down Expand Up @@ -357,7 +393,8 @@ impl ExecutionLayer {
///
/// https://github.com/ethereum/consensus-specs/blob/v1.1.0/specs/merge/validator.md
pub async fn get_terminal_pow_block_hash(&self) -> Result<Option<Hash256>, Error> {
self.engines()
let hash_opt = self
.engines()
.first_success(|engine| async move {
if self.terminal_block_hash() != Hash256::zero() {
// Note: the specification is written such that if there are multiple blocks in
Expand All @@ -376,7 +413,19 @@ impl ExecutionLayer {
}
})
.await
.map_err(Error::EngineErrors)
.map_err(Error::EngineErrors)?;

if let Some(hash) = &hash_opt {
info!(
self.log(),
"Found terminal block hash";
"terminal_block_hash_override" => ?self.terminal_block_hash(),
"terminal_total_difficulty" => ?self.terminal_total_difficulty(),
"block_hash" => ?hash,
);
}

Ok(hash_opt)
}

/// This function should remain internal. External users should use
Expand Down
14 changes: 11 additions & 3 deletions beacon_node/genesis/src/interop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@ use ssz::Encode;
use state_processing::initialize_beacon_state_from_eth1;
use types::{BeaconState, ChainSpec, DepositData, EthSpec, Hash256, Keypair, PublicKey, Signature};

pub const DEFAULT_ETH1_BLOCK_HASH: &[u8] = &[0x42; 32];

/// Builds a genesis state as defined by the Eth2 interop procedure (see below).
///
/// Reference:
/// https://github.com/ethereum/eth2.0-pm/tree/6e41fcf383ebeb5125938850d8e9b4e9888389b4/interop/mocked_start
pub fn interop_genesis_state<T: EthSpec>(
keypairs: &[Keypair],
genesis_time: u64,
eth1_block_hash: Hash256,
spec: &ChainSpec,
) -> Result<BeaconState<T>, String> {
let eth1_block_hash = Hash256::from_slice(&[0x42; 32]);
let eth1_block_hash = eth1_block_hash;
let eth1_timestamp = 2_u64.pow(40);
let amount = spec.max_effective_balance;

Expand Down Expand Up @@ -73,8 +76,13 @@ mod test {

let keypairs = generate_deterministic_keypairs(validator_count);

let state = interop_genesis_state::<TestEthSpec>(&keypairs, genesis_time, spec)
.expect("should build state");
let state = interop_genesis_state::<TestEthSpec>(
&keypairs,
genesis_time,
Hash256::from_slice(DEFAULT_ETH1_BLOCK_HASH),
spec,
)
.expect("should build state");

assert_eq!(
state.eth1_data().block_hash,
Expand Down
2 changes: 1 addition & 1 deletion beacon_node/genesis/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@ mod interop;

pub use eth1::Config as Eth1Config;
pub use eth1_genesis_service::{Eth1GenesisService, Statistics};
pub use interop::interop_genesis_state;
pub use interop::{interop_genesis_state, DEFAULT_ETH1_BLOCK_HASH};
pub use types::test_utils::generate_deterministic_keypairs;
76 changes: 42 additions & 34 deletions beacon_node/http_api/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ pub struct Config {
pub listen_port: u16,
pub allow_origin: Option<String>,
pub serve_legacy_spec: bool,
pub allow_sync_stalled: bool,
}

impl Default for Config {
Expand All @@ -91,6 +92,7 @@ impl Default for Config {
listen_port: 5052,
allow_origin: None,
serve_legacy_spec: true,
allow_sync_stalled: false,
}
}
}
Expand Down Expand Up @@ -220,6 +222,7 @@ pub fn serve<T: BeaconChainTypes>(
shutdown: impl Future<Output = ()> + Send + Sync + 'static,
) -> Result<(SocketAddr, impl Future<Output = ()>), Error> {
let config = ctx.config.clone();
let allow_sync_stalled = config.allow_sync_stalled;
let log = ctx.log.clone();

// Configure CORS.
Expand Down Expand Up @@ -321,44 +324,49 @@ pub fn serve<T: BeaconChainTypes>(
});

// Create a `warp` filter that rejects request whilst the node is syncing.
let not_while_syncing_filter = warp::any()
.and(network_globals.clone())
.and(chain_filter.clone())
.and_then(
|network_globals: Arc<NetworkGlobals<T::EthSpec>>, chain: Arc<BeaconChain<T>>| async move {
match *network_globals.sync_state.read() {
SyncState::SyncingFinalized { .. } => {
let head_slot = chain.best_slot().map_err(warp_utils::reject::beacon_chain_error)?;

let current_slot = chain
.slot_clock
.now_or_genesis()
.ok_or_else(|| {
warp_utils::reject::custom_server_error(
"unable to read slot clock".to_string(),
)
})?;
let not_while_syncing_filter =
warp::any()
.and(network_globals.clone())
.and(chain_filter.clone())
.and_then(
move |network_globals: Arc<NetworkGlobals<T::EthSpec>>,
chain: Arc<BeaconChain<T>>| async move {
match *network_globals.sync_state.read() {
SyncState::SyncingFinalized { .. } => {
let head_slot = chain
.best_slot()
.map_err(warp_utils::reject::beacon_chain_error)?;

let tolerance = SYNC_TOLERANCE_EPOCHS * T::EthSpec::slots_per_epoch();
let current_slot =
chain.slot_clock.now_or_genesis().ok_or_else(|| {
warp_utils::reject::custom_server_error(
"unable to read slot clock".to_string(),
)
})?;

if head_slot + tolerance >= current_slot {
Ok(())
} else {
Err(warp_utils::reject::not_synced(format!(
"head slot is {}, current slot is {}",
head_slot, current_slot
)))
let tolerance = SYNC_TOLERANCE_EPOCHS * T::EthSpec::slots_per_epoch();

if head_slot + tolerance >= current_slot {
Ok(())
} else {
Err(warp_utils::reject::not_synced(format!(
"head slot is {}, current slot is {}",
head_slot, current_slot
)))
}
}
SyncState::SyncingHead { .. }
| SyncState::SyncTransition
| SyncState::BackFillSyncing { .. } => Ok(()),
SyncState::Synced => Ok(()),
SyncState::Stalled if allow_sync_stalled => Ok(()),
SyncState::Stalled => Err(warp_utils::reject::not_synced(
"sync is stalled".to_string(),
)),
}
SyncState::SyncingHead { .. } | SyncState::SyncTransition | SyncState::BackFillSyncing { .. } => Ok(()),
SyncState::Synced => Ok(()),
SyncState::Stalled => Err(warp_utils::reject::not_synced(
"sync is stalled".to_string(),
)),
}
},
)
.untuple_one();
},
)
.untuple_one();

// Create a `warp` filter that provides access to the logger.
let inner_ctx = ctx.clone();
Expand Down
1 change: 1 addition & 0 deletions beacon_node/http_api/tests/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ pub fn create_api_server<T: BeaconChainTypes>(
listen_port: 0,
allow_origin: None,
serve_legacy_spec: true,
allow_sync_stalled: false,
},
chain: Some(chain.clone()),
network_tx: Some(network_tx),
Expand Down
Loading

0 comments on commit dc6aa25

Please sign in to comment.