Skip to content

Commit c9d8097

Browse files
paulhaunerjxs
authored andcommitted
Add Holesky (sigp#4653)
## Issue Addressed NA ## Proposed Changes Add the Holesky network config as per https://github.com/eth-clients/holesky/tree/36e4ff2d5138dcb2eb614f0f60fdb060b2adc1e2/custom_config_data. Since the genesis state is ~190MB, I've opted to *not* include it in the binary and instead download it at runtime (see sigp#4564 for context). To download this file we have: - A hard-coded URL for a SigP-hosted S3 bucket with the Holesky genesis state. Assuming this download works correctly, users will be none the wiser that the state wasn't included in the binary (apart from some additional logs) - If the user provides a `--checkpoint-sync-url` flag, then LH will download the genesis state from that server rather than our S3 bucket. - If the user provides a `--genesis-state-url` flag, then LH will download the genesis state from that server regardless of the S3 bucket or `--checkpoint-sync-url` flag. - Whenever a genesis state is downloaded it is checked against a checksum baked into the binary. - A genesis state will never be downloaded if it's already included in the binary. - There is a `--genesis-state-url-timeout` flag to tweak the timeout for downloading the genesis state file. ## Log Output Example of log output when a state is downloaded: ```bash Aug 23 05:40:13.424 INFO Logging to file path: "/Users/paul/.lighthouse/holesky/beacon/logs/beacon.log" Aug 23 05:40:13.425 INFO Lighthouse started version: Lighthouse/v4.3.0-bd9931f+ Aug 23 05:40:13.425 INFO Configured for network name: holesky Aug 23 05:40:13.426 INFO Data directory initialised datadir: /Users/paul/.lighthouse/holesky Aug 23 05:40:13.427 INFO Deposit contract address: 0x4242424242424242424242424242424242424242, deploy_block: 0 Aug 23 05:40:13.427 INFO Downloading genesis state info: this may take some time on testnets with large validator counts, timeout: 60s, server: https://sigp-public-genesis-states.s3.ap-southeast-2.amazonaws.com/ Aug 23 05:40:29.895 INFO Starting from known genesis state service: beacon ``` Example of log output when there are no URLs specified: ``` Aug 23 06:29:51.645 INFO Logging to file path: "/Users/paul/.lighthouse/goerli/beacon/logs/beacon.log" Aug 23 06:29:51.646 INFO Lighthouse started version: Lighthouse/v4.3.0-666a39c+ Aug 23 06:29:51.646 INFO Configured for network name: goerli Aug 23 06:29:51.647 INFO Data directory initialised datadir: /Users/paul/.lighthouse/goerli Aug 23 06:29:51.647 INFO Deposit contract address: 0xff50ed3d0ec03ac01d4c79aad74928bff48a7b2b, deploy_block: 4367322 The genesis state is not present in the binary and there are no known download URLs. Please use --checkpoint-sync-url or --genesis-state-url. ``` ## Additional Info I tested the `--genesis-state-url` flag with all 9 Goerli checkpoint sync servers on https://eth-clients.github.io/checkpoint-sync-endpoints/ and they all worked 🎉 My IDE eagerly formatted some `Cargo.toml`. I've disabled it but I don't see the value in spending time reverting the changes that are already there. I also added the `GenesisStateBytes` enum to avoid an unnecessary clone on the genesis state bytes baked into the binary. This is not a huge deal on Mainnet, but will become more relevant when testing with big genesis states. When we do a fresh checkpoint sync we're downloading the genesis state to check the `genesis_validators_root` against the finalised state we receive. This is not *entirely* pointless, since we verify the checksum when we download the genesis state so we are actually guaranteeing that the finalised state is on the same network. There might be a smarter/less-download-y way to go about this, but I've run out of cycles to figure that out. Perhaps we can grab it in the next release?
1 parent f592c38 commit c9d8097

File tree

22 files changed

+651
-117
lines changed

22 files changed

+651
-117
lines changed

Cargo.lock

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

account_manager/Cargo.toml

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
[package]
22
name = "account_manager"
33
version = "0.3.5"
4-
authors = ["Paul Hauner <[email protected]>", "Luke Anderson <[email protected]>"]
4+
authors = [
5+
"Paul Hauner <[email protected]>",
6+
"Luke Anderson <[email protected]>",
7+
]
58
edition = "2021"
69

710
[dependencies]
@@ -19,13 +22,14 @@ tokio = { version = "1.14.0", features = ["full"] }
1922
eth2_keystore = { path = "../crypto/eth2_keystore" }
2023
account_utils = { path = "../common/account_utils" }
2124
slashing_protection = { path = "../validator_client/slashing_protection" }
22-
eth2 = {path = "../common/eth2"}
23-
safe_arith = {path = "../consensus/safe_arith"}
25+
eth2 = { path = "../common/eth2" }
26+
safe_arith = { path = "../consensus/safe_arith" }
2427
slot_clock = { path = "../common/slot_clock" }
2528
filesystem = { path = "../common/filesystem" }
2629
sensitive_url = { path = "../common/sensitive_url" }
2730
serde = { version = "1.0.116", features = ["derive"] }
2831
serde_json = "1.0.58"
32+
slog = { version = "2.5.2" }
2933

3034
[dev-dependencies]
3135
tempfile = "3.1.0"

account_manager/src/validator/exit.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use eth2_keystore::Keystore;
1010
use eth2_network_config::Eth2NetworkConfig;
1111
use safe_arith::SafeArith;
1212
use sensitive_url::SensitiveUrl;
13+
use slog::Logger;
1314
use slot_clock::{SlotClock, SystemTimeSlotClock};
1415
use std::path::{Path, PathBuf};
1516
use std::time::Duration;
@@ -78,6 +79,12 @@ pub fn cli_run<E: EthSpec>(matches: &ArgMatches, env: Environment<E>) -> Result<
7879
let password_file_path: Option<PathBuf> =
7980
clap_utils::parse_optional(matches, PASSWORD_FILE_FLAG)?;
8081

82+
let genesis_state_url: Option<String> =
83+
clap_utils::parse_optional(matches, "genesis-state-url")?;
84+
let genesis_state_url_timeout =
85+
clap_utils::parse_required(matches, "genesis-state-url-timeout")
86+
.map(Duration::from_secs)?;
87+
8188
let stdin_inputs = cfg!(windows) || matches.is_present(STDIN_INPUTS_FLAG);
8289
let no_wait = matches.is_present(NO_WAIT);
8390
let no_confirmation = matches.is_present(NO_CONFIRMATION);
@@ -104,6 +111,9 @@ pub fn cli_run<E: EthSpec>(matches: &ArgMatches, env: Environment<E>) -> Result<
104111
&eth2_network_config,
105112
no_wait,
106113
no_confirmation,
114+
genesis_state_url,
115+
genesis_state_url_timeout,
116+
env.core_context().log(),
107117
))?;
108118

109119
Ok(())
@@ -120,13 +130,14 @@ async fn publish_voluntary_exit<E: EthSpec>(
120130
eth2_network_config: &Eth2NetworkConfig,
121131
no_wait: bool,
122132
no_confirmation: bool,
133+
genesis_state_url: Option<String>,
134+
genesis_state_url_timeout: Duration,
135+
log: &Logger,
123136
) -> Result<(), String> {
124137
let genesis_data = get_geneisis_data(client).await?;
125138
let testnet_genesis_root = eth2_network_config
126-
.beacon_state::<E>()
127-
.as_ref()
128-
.expect("network should have valid genesis state")
129-
.genesis_validators_root();
139+
.genesis_validators_root::<E>(genesis_state_url.as_deref(), genesis_state_url_timeout, log)?
140+
.ok_or("Genesis state is unknown")?;
130141

131142
// Verify that the beacon node and validator being exited are on the same network.
132143
if genesis_data.genesis_validators_root != testnet_genesis_root {

account_manager/src/validator/slashing_protection.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use slashing_protection::{
77
use std::fs::File;
88
use std::path::PathBuf;
99
use std::str::FromStr;
10-
use types::{BeaconState, Epoch, EthSpec, PublicKeyBytes, Slot};
10+
use std::time::Duration;
11+
use types::{Epoch, EthSpec, PublicKeyBytes, Slot};
1112

1213
pub const CMD: &str = "slashing-protection";
1314
pub const IMPORT_CMD: &str = "import";
@@ -82,19 +83,24 @@ pub fn cli_run<T: EthSpec>(
8283
) -> Result<(), String> {
8384
let slashing_protection_db_path = validator_base_dir.join(SLASHING_PROTECTION_FILENAME);
8485

86+
let genesis_state_url: Option<String> =
87+
clap_utils::parse_optional(matches, "genesis-state-url")?;
88+
let genesis_state_url_timeout =
89+
clap_utils::parse_required(matches, "genesis-state-url-timeout")
90+
.map(Duration::from_secs)?;
91+
92+
let context = env.core_context();
8593
let eth2_network_config = env
8694
.eth2_network_config
8795
.ok_or("Unable to get testnet configuration from the environment")?;
8896

8997
let genesis_validators_root = eth2_network_config
90-
.beacon_state::<T>()
91-
.map(|state: BeaconState<T>| state.genesis_validators_root())
92-
.map_err(|e| {
93-
format!(
94-
"Unable to get genesis state, has genesis occurred? Detail: {:?}",
95-
e
96-
)
97-
})?;
98+
.genesis_validators_root::<T>(
99+
genesis_state_url.as_deref(),
100+
genesis_state_url_timeout,
101+
context.log(),
102+
)?
103+
.ok_or_else(|| "Unable to get genesis state, has genesis occurred?".to_string())?;
98104

99105
match matches.subcommand() {
100106
(IMPORT_CMD, Some(matches)) => {

beacon_node/Cargo.toml

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
[package]
22
name = "beacon_node"
33
version = "4.3.0"
4-
authors = ["Paul Hauner <[email protected]>", "Age Manning <[email protected]"]
4+
authors = [
5+
"Paul Hauner <[email protected]>",
6+
"Age Manning <[email protected]",
7+
]
58
edition = "2021"
69

710
[lib]
@@ -12,7 +15,9 @@ path = "src/lib.rs"
1215
node_test_rig = { path = "../testing/node_test_rig" }
1316

1417
[features]
15-
write_ssz_files = ["beacon_chain/write_ssz_files"] # Writes debugging .ssz files to /tmp during block processing.
18+
write_ssz_files = [
19+
"beacon_chain/write_ssz_files",
20+
] # Writes debugging .ssz files to /tmp during block processing.
1621

1722
[dependencies]
1823
eth2_config = { path = "../common/eth2_config" }
@@ -21,9 +26,12 @@ types = { path = "../consensus/types" }
2126
store = { path = "./store" }
2227
client = { path = "client" }
2328
clap = "2.33.3"
24-
slog = { version = "2.5.2", features = ["max_level_trace", "release_max_level_trace"] }
29+
slog = { version = "2.5.2", features = [
30+
"max_level_trace",
31+
"release_max_level_trace",
32+
] }
2533
dirs = "3.0.1"
26-
directory = {path = "../common/directory"}
34+
directory = { path = "../common/directory" }
2735
futures = "0.3.7"
2836
environment = { path = "../lighthouse/environment" }
2937
task_executor = { path = "../common/task_executor" }
@@ -41,4 +49,4 @@ monitoring_api = { path = "../common/monitoring_api" }
4149
sensitive_url = { path = "../common/sensitive_url" }
4250
http_api = { path = "http_api" }
4351
unused_port = { path = "../common/unused_port" }
44-
strum = "0.24.1"
52+
strum = "0.24.1"

beacon_node/client/src/builder.rs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ where
154154
let runtime_context =
155155
runtime_context.ok_or("beacon_chain_start_method requires a runtime context")?;
156156
let context = runtime_context.service_context("beacon".into());
157+
let log = context.log();
157158
let spec = chain_spec.ok_or("beacon_chain_start_method requires a chain spec")?;
158159
let event_handler = if self.http_api_config.enabled {
159160
Some(ServerSentEventHandler::new(
@@ -164,7 +165,7 @@ where
164165
None
165166
};
166167

167-
let execution_layer = if let Some(config) = config.execution_layer {
168+
let execution_layer = if let Some(config) = config.execution_layer.clone() {
168169
let context = runtime_context.service_context("exec".into());
169170
let execution_layer = ExecutionLayer::from_config(
170171
config,
@@ -249,23 +250,19 @@ where
249250
)?;
250251
builder.genesis_state(genesis_state).map(|v| (v, None))?
251252
}
252-
ClientGenesis::SszBytes {
253-
genesis_state_bytes,
254-
} => {
253+
ClientGenesis::GenesisState => {
255254
info!(
256255
context.log(),
257256
"Starting from known genesis state";
258257
);
259258

260-
let genesis_state = BeaconState::from_ssz_bytes(&genesis_state_bytes, &spec)
261-
.map_err(|e| format!("Unable to parse genesis state SSZ: {:?}", e))?;
259+
let genesis_state = genesis_state(&runtime_context, &config, log)?;
262260

263261
builder.genesis_state(genesis_state).map(|v| (v, None))?
264262
}
265263
ClientGenesis::WeakSubjSszBytes {
266264
anchor_state_bytes,
267265
anchor_block_bytes,
268-
genesis_state_bytes,
269266
} => {
270267
info!(context.log(), "Starting checkpoint sync");
271268
if config.chain.genesis_backfill {
@@ -279,17 +276,13 @@ where
279276
.map_err(|e| format!("Unable to parse weak subj state SSZ: {:?}", e))?;
280277
let anchor_block = SignedBeaconBlock::from_ssz_bytes(&anchor_block_bytes, &spec)
281278
.map_err(|e| format!("Unable to parse weak subj block SSZ: {:?}", e))?;
282-
let genesis_state = BeaconState::from_ssz_bytes(&genesis_state_bytes, &spec)
283-
.map_err(|e| format!("Unable to parse genesis state SSZ: {:?}", e))?;
279+
let genesis_state = genesis_state(&runtime_context, &config, log)?;
284280

285281
builder
286282
.weak_subjectivity_state(anchor_state, anchor_block, genesis_state)
287283
.map(|v| (v, None))?
288284
}
289-
ClientGenesis::CheckpointSyncUrl {
290-
genesis_state_bytes,
291-
url,
292-
} => {
285+
ClientGenesis::CheckpointSyncUrl { url } => {
293286
info!(
294287
context.log(),
295288
"Starting checkpoint sync";
@@ -384,8 +377,7 @@ where
384377

385378
debug!(context.log(), "Downloaded finalized block");
386379

387-
let genesis_state = BeaconState::from_ssz_bytes(&genesis_state_bytes, &spec)
388-
.map_err(|e| format!("Unable to parse genesis state SSZ: {:?}", e))?;
380+
let genesis_state = genesis_state(&runtime_context, &config, log)?;
389381

390382
info!(
391383
context.log(),
@@ -1089,3 +1081,22 @@ where
10891081
Ok(self)
10901082
}
10911083
}
1084+
1085+
/// Obtain the genesis state from the `eth2_network_config` in `context`.
1086+
fn genesis_state<T: EthSpec>(
1087+
context: &RuntimeContext<T>,
1088+
config: &ClientConfig,
1089+
log: &Logger,
1090+
) -> Result<BeaconState<T>, String> {
1091+
let eth2_network_config = context
1092+
.eth2_network_config
1093+
.as_ref()
1094+
.ok_or("An eth2_network_config is required to obtain the genesis state")?;
1095+
eth2_network_config
1096+
.genesis_state::<T>(
1097+
config.genesis_state_url.as_deref(),
1098+
config.genesis_state_url_timeout,
1099+
log,
1100+
)?
1101+
.ok_or_else(|| "Genesis state is unknown".to_string())
1102+
}

beacon_node/client/src/config.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use sensitive_url::SensitiveUrl;
77
use serde_derive::{Deserialize, Serialize};
88
use std::fs;
99
use std::path::PathBuf;
10+
use std::time::Duration;
1011
use types::{Graffiti, PublicKeyBytes};
1112
/// Default directory name for the freezer database under the top-level data dir.
1213
const DEFAULT_FREEZER_DB_DIR: &str = "freezer_db";
@@ -25,18 +26,13 @@ pub enum ClientGenesis {
2526
/// contract.
2627
#[default]
2728
DepositContract,
28-
/// Loads the genesis state from SSZ-encoded `BeaconState` bytes.
29-
///
30-
/// We include the bytes instead of the `BeaconState<E>` because the `EthSpec` type
31-
/// parameter would be very annoying.
32-
SszBytes { genesis_state_bytes: Vec<u8> },
29+
/// Loads the genesis state from the genesis state in the `Eth2NetworkConfig`.
30+
GenesisState,
3331
WeakSubjSszBytes {
34-
genesis_state_bytes: Vec<u8>,
3532
anchor_state_bytes: Vec<u8>,
3633
anchor_block_bytes: Vec<u8>,
3734
},
3835
CheckpointSyncUrl {
39-
genesis_state_bytes: Vec<u8>,
4036
url: SensitiveUrl,
4137
},
4238
}
@@ -81,6 +77,8 @@ pub struct Config {
8177
pub slasher: Option<slasher::Config>,
8278
pub logger_config: LoggerConfig,
8379
pub beacon_processor: BeaconProcessorConfig,
80+
pub genesis_state_url: Option<String>,
81+
pub genesis_state_url_timeout: Duration,
8482
}
8583

8684
impl Default for Config {
@@ -108,6 +106,9 @@ impl Default for Config {
108106
validator_monitor_individual_tracking_threshold: DEFAULT_INDIVIDUAL_TRACKING_THRESHOLD,
109107
logger_config: LoggerConfig::default(),
110108
beacon_processor: <_>::default(),
109+
genesis_state_url: <_>::default(),
110+
// This default value should always be overwritten by the CLI default value.
111+
genesis_state_url_timeout: Duration::from_secs(60),
111112
}
112113
}
113114
}

beacon_node/src/config.rs

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -471,9 +471,30 @@ pub fn get_config<E: EthSpec>(
471471
client_config.chain.checkpoint_sync_url_timeout =
472472
clap_utils::parse_required::<u64>(cli_args, "checkpoint-sync-url-timeout")?;
473473

474-
client_config.genesis = if let Some(genesis_state_bytes) =
475-
eth2_network_config.genesis_state_bytes.clone()
476-
{
474+
client_config.genesis_state_url_timeout =
475+
clap_utils::parse_required(cli_args, "genesis-state-url-timeout")
476+
.map(Duration::from_secs)?;
477+
478+
let genesis_state_url_opt =
479+
clap_utils::parse_optional::<String>(cli_args, "genesis-state-url")?;
480+
let checkpoint_sync_url_opt =
481+
clap_utils::parse_optional::<String>(cli_args, "checkpoint-sync-url")?;
482+
483+
// If the `--genesis-state-url` is defined, use that to download the
484+
// genesis state bytes. If it's not defined, try `--checkpoint-sync-url`.
485+
client_config.genesis_state_url = if let Some(genesis_state_url) = genesis_state_url_opt {
486+
Some(genesis_state_url)
487+
} else if let Some(checkpoint_sync_url) = checkpoint_sync_url_opt {
488+
// If the checkpoint sync URL is going to be used to download the
489+
// genesis state, adopt the timeout from the checkpoint sync URL too.
490+
client_config.genesis_state_url_timeout =
491+
Duration::from_secs(client_config.chain.checkpoint_sync_url_timeout);
492+
Some(checkpoint_sync_url)
493+
} else {
494+
None
495+
};
496+
497+
client_config.genesis = if eth2_network_config.genesis_state_is_known() {
477498
// Set up weak subjectivity sync, or start from the hardcoded genesis state.
478499
if let (Some(initial_state_path), Some(initial_block_path)) = (
479500
cli_args.value_of("checkpoint-state"),
@@ -495,25 +516,16 @@ pub fn get_config<E: EthSpec>(
495516
let anchor_block_bytes = read(initial_block_path)?;
496517

497518
ClientGenesis::WeakSubjSszBytes {
498-
genesis_state_bytes,
499519
anchor_state_bytes,
500520
anchor_block_bytes,
501521
}
502522
} else if let Some(remote_bn_url) = cli_args.value_of("checkpoint-sync-url") {
503523
let url = SensitiveUrl::parse(remote_bn_url)
504524
.map_err(|e| format!("Invalid checkpoint sync URL: {:?}", e))?;
505525

506-
ClientGenesis::CheckpointSyncUrl {
507-
genesis_state_bytes,
508-
url,
509-
}
526+
ClientGenesis::CheckpointSyncUrl { url }
510527
} else {
511-
// Note: re-serializing the genesis state is not so efficient, however it avoids adding
512-
// trait bounds to the `ClientGenesis` enum. This would have significant flow-on
513-
// effects.
514-
ClientGenesis::SszBytes {
515-
genesis_state_bytes,
516-
}
528+
ClientGenesis::GenesisState
517529
}
518530
} else {
519531
if cli_args.is_present("checkpoint-state") || cli_args.is_present("checkpoint-sync-url") {

0 commit comments

Comments
 (0)