Merge unstable 20230911 into deneb-free-blobs.

This commit is contained in:
Jimmy Chen
2023-09-11 11:59:13 +10:00
71 changed files with 2298 additions and 956 deletions

View File

@@ -7,8 +7,8 @@ edition = "2021"
build = "build.rs"
[build-dependencies]
zip = "0.5.8"
eth2_config = { path = "../eth2_config"}
zip = "0.6"
eth2_config = { path = "../eth2_config" }
[dev-dependencies]
tempfile = "3.1.0"
@@ -16,8 +16,15 @@ tempfile = "3.1.0"
[dependencies]
serde_yaml = "0.8.13"
serde_json = "1.0.58"
types = { path = "../../consensus/types"}
types = { path = "../../consensus/types" }
kzg = { path = "../../crypto/kzg" }
ethereum_ssz = "0.5.0"
eth2_config = { path = "../eth2_config"}
discv5 = "0.3.1"
eth2_config = { path = "../eth2_config" }
discv5 = "0.3.1"
reqwest = { version = "0.11.0", features = ["blocking"] }
pretty_reqwest_error = { path = "../pretty_reqwest_error" }
sha2 = "0.10"
url = "2.2.2"
sensitive_url = { path = "../sensitive_url" }
slog = "2.5.2"
logging = { path = "../logging" }

View File

@@ -1,5 +1,7 @@
//! Extracts zipped genesis states on first run.
use eth2_config::{Eth2NetArchiveAndDirectory, ETH2_NET_DIRS, GENESIS_FILE_NAME};
use eth2_config::{
Eth2NetArchiveAndDirectory, GenesisStateSource, ETH2_NET_DIRS, GENESIS_FILE_NAME,
};
use std::fs::File;
use std::io;
use zip::ZipArchive;
@@ -26,7 +28,7 @@ fn uncompress_state(network: &Eth2NetArchiveAndDirectory<'static>) -> Result<(),
return Ok(());
}
if network.genesis_is_known {
if network.genesis_state_source == GenesisStateSource::IncludedBytes {
// Extract genesis state from genesis.ssz.zip
let archive_path = network.genesis_state_archive();
let archive_file = File::open(&archive_path)
@@ -46,7 +48,8 @@ fn uncompress_state(network: &Eth2NetArchiveAndDirectory<'static>) -> Result<(),
io::copy(&mut file, &mut outfile)
.map_err(|e| format!("Error writing file {:?}: {}", genesis_ssz_path, e))?;
} else {
// Create empty genesis.ssz if genesis is unknown
// Create empty genesis.ssz if genesis is unknown or to be downloaded via URL.
// This is a bit of a hack to make `include_bytes!` easier to deal with.
File::create(genesis_ssz_path)
.map_err(|e| format!("Failed to create {}: {}", GENESIS_FILE_NAME, e))?;
}

View File

@@ -0,0 +1,8 @@
# chiado-teku-0
- "enr:-Ly4QLYLNqrjvSxD3lpAPBUNlxa6cIbe79JqLZLFcZZjWoCjZcw-85agLUErHiygG2weRSCLnd5V460qTbLbwJQsfZkoh2F0dG5ldHOI__________-EZXRoMpAxNnBDAgAAb___________gmlkgnY0gmlwhKq7mu-Jc2VjcDI1NmsxoQP900YAYa9kdvzlSKGjVo-F3XVzATjOYp3BsjLjSophO4hzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA"
# chiado-teku-1
- "enr:-Ly4QCGeYvTCNOGKi0mKRUd45rLj96b4pH98qG7B9TCUGXGpHZALtaL2-XfjASQyhbCqENccI4PGXVqYTIehNT9KJMQgh2F0dG5ldHOI__________-EZXRoMpAxNnBDAgAAb___________gmlkgnY0gmlwhIuQrVSJc2VjcDI1NmsxoQP9iDchx2PGl3JyJ29B9fhLCvVMN6n23pPAIIeFV-sHOIhzeW5jbmV0cw-DdGNwgiMog3VkcIIjKA"
#GnosisDAO Bootnode: 3.71.132.231
- "enr:-Ly4QAtr21x5Ps7HYhdZkIBRBgcBkvlIfEel1YNjtFWf4cV3au2LgBGICz9PtEs9-p2HUl_eME8m1WImxTxSB3AkCMwBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAxNnBDAgAAb___________gmlkgnY0gmlwhANHhOeJc2VjcDI1NmsxoQNLp1QPV8-pyMCohOtj6xGtSBM_GtVTqzlbvNsCF4ezkYhzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA"
#GnosisDAO Bootnode: 3.69.35.13
- "enr:-Ly4QLgn8Bx6faigkKUGZQvd1HDToV2FAxZIiENK-lczruzQb90qJK-4E65ADly0s4__dQOW7IkLMW7ZAyJy2vtiLy8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAxNnBDAgAAb___________gmlkgnY0gmlwhANFIw2Jc2VjcDI1NmsxoQMa-fWEy9UJHfOl_lix3wdY5qust78sHAqZnWwEiyqKgYhzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA"

View File

@@ -0,0 +1,154 @@
# Extends the mainnet preset
PRESET_BASE: gnosis
# needs to exist because of Prysm. Otherwise it conflicts with mainnet genesis
CONFIG_NAME: chiado
# Genesis
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 6000
# 10 October 2022 10:00:00 GMT+0000
MIN_GENESIS_TIME: 1665396000
GENESIS_DELAY: 300
# Projected time: 2022-11-04T15:00:00.000Z, block: 680928
TERMINAL_TOTAL_DIFFICULTY: 231707791542740786049188744689299064356246512
# Deposit contract
# ---------------------------------------------------------------
# NOTE: Don't use a value too high, or Teku rejects it (4294906129 NOK)
DEPOSIT_CHAIN_ID: 10200
DEPOSIT_NETWORK_ID: 10200
DEPOSIT_CONTRACT_ADDRESS: 0xb97036A26259B7147018913bD58a774cf91acf25
# Misc
# ---------------------------------------------------------------
# 2**6 (= 64)
MAX_COMMITTEES_PER_SLOT: 64
# 2**7 (= 128)
TARGET_COMMITTEE_SIZE: 128
# 2**11 (= 2,048)
MAX_VALIDATORS_PER_COMMITTEE: 2048
# 2**2 (= 4)
MIN_PER_EPOCH_CHURN_LIMIT: 4
# 2**12 (= 4096)
CHURN_LIMIT_QUOTIENT: 4096
# See issue 563
SHUFFLE_ROUND_COUNT: 90
# 4
HYSTERESIS_QUOTIENT: 4
# 1 (minus 0.25)
HYSTERESIS_DOWNWARD_MULTIPLIER: 1
# 5 (plus 1.25)
HYSTERESIS_UPWARD_MULTIPLIER: 5
# Validator
# ---------------------------------------------------------------
# 2**10 (= 1024) ~1.4 hour
ETH1_FOLLOW_DISTANCE: 1024
# 2**4 (= 16)
TARGET_AGGREGATORS_PER_COMMITTEE: 16
# 2**0 (= 1)
RANDOM_SUBNETS_PER_VALIDATOR: 1
# 2**8 (= 256)
EPOCHS_PER_RANDOM_SUBNET_SUBSCRIPTION: 256
# 6 (estimate from xDai mainnet)
SECONDS_PER_ETH1_BLOCK: 6
# Gwei values
# ---------------------------------------------------------------
# 2**0 * 10**9 (= 1,000,000,000) Gwei
MIN_DEPOSIT_AMOUNT: 1000000000
# 2**5 * 10**9 (= 32,000,000,000) Gwei
MAX_EFFECTIVE_BALANCE: 32000000000
# 2**4 * 10**9 (= 16,000,000,000) Gwei
EJECTION_BALANCE: 16000000000
# 2**0 * 10**9 (= 1,000,000,000) Gwei
EFFECTIVE_BALANCE_INCREMENT: 1000000000
# Initial values
# ---------------------------------------------------------------
# GBC area code
GENESIS_FORK_VERSION: 0x0000006f
BLS_WITHDRAWAL_PREFIX: 0x00
# Time parameters
# ---------------------------------------------------------------
# 5 seconds
SECONDS_PER_SLOT: 5
# 2**0 (= 1) slots 12 seconds
MIN_ATTESTATION_INCLUSION_DELAY: 1
# 2**4 (= 16) slots 1.87 minutes
SLOTS_PER_EPOCH: 16
# 2**0 (= 1) epochs 1.87 minutes
MIN_SEED_LOOKAHEAD: 1
# 2**2 (= 4) epochs 7.47 minutes
MAX_SEED_LOOKAHEAD: 4
# 2**6 (= 64) epochs ~2 hours
EPOCHS_PER_ETH1_VOTING_PERIOD: 64
# 2**13 (= 8,192) slots ~15.9 hours
SLOTS_PER_HISTORICAL_ROOT: 8192
# 2**8 (= 256) epochs ~8 hours
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
# 2**8 (= 256) epochs ~8 hours
SHARD_COMMITTEE_PERIOD: 256
# 2**2 (= 4) epochs 7.47 minutes
MIN_EPOCHS_TO_INACTIVITY_PENALTY: 4
# State vector lengths
# ---------------------------------------------------------------
# 2**16 (= 65,536) epochs ~85 days
EPOCHS_PER_HISTORICAL_VECTOR: 65536
# 2**13 (= 8,192) epochs ~10.6 days
EPOCHS_PER_SLASHINGS_VECTOR: 8192
# 2**24 (= 16,777,216) historical roots, ~15,243 years
HISTORICAL_ROOTS_LIMIT: 16777216
# 2**40 (= 1,099,511,627,776) validator spots
VALIDATOR_REGISTRY_LIMIT: 1099511627776
# Reward and penalty quotients
# ---------------------------------------------------------------
# 25
BASE_REWARD_FACTOR: 25
# 2**9 (= 512)
WHISTLEBLOWER_REWARD_QUOTIENT: 512
# 2**3 (= 8)
PROPOSER_REWARD_QUOTIENT: 8
# 2**26 (= 67,108,864)
INACTIVITY_PENALTY_QUOTIENT: 67108864
# 2**7 (= 128) (lower safety margin at Phase 0 genesis)
MIN_SLASHING_PENALTY_QUOTIENT: 128
# 1 (lower safety margin at Phase 0 genesis)
PROPORTIONAL_SLASHING_MULTIPLIER: 1
# Max operations per block
# ---------------------------------------------------------------
# 2**4 (= 16)
MAX_PROPOSER_SLASHINGS: 16
# 2**1 (= 2)
MAX_ATTESTER_SLASHINGS: 2
# 2**7 (= 128)
MAX_ATTESTATIONS: 128
# 2**4 (= 16)
MAX_DEPOSITS: 16
# 2**4 (= 16)
MAX_VOLUNTARY_EXITS: 16
# Signature domains
# ---------------------------------------------------------------
DOMAIN_BEACON_PROPOSER: 0x00000000
DOMAIN_BEACON_ATTESTER: 0x01000000
DOMAIN_RANDAO: 0x02000000
DOMAIN_DEPOSIT: 0x03000000
DOMAIN_VOLUNTARY_EXIT: 0x04000000
DOMAIN_SELECTION_PROOF: 0x05000000
DOMAIN_AGGREGATE_AND_PROOF: 0x06000000
DOMAIN_SYNC_COMMITTEE: 0x07000000
DOMAIN_SYNC_COMMITTEE_SELECTION_PROOF: 0x08000000
DOMAIN_CONTRIBUTION_AND_PROOF: 0x09000000
# Altair
ALTAIR_FORK_VERSION: 0x0100006f
ALTAIR_FORK_EPOCH: 90 # Mon Oct 10 2022 12:00:00 GMT+0000
# Bellatrix
BELLATRIX_FORK_VERSION: 0x0200006f
BELLATRIX_FORK_EPOCH: 180 # Mon Oct 10 2022 14:00:00 GMT+0000
# Capella
CAPELLA_FORK_VERSION: 0x0300006f
CAPELLA_FORK_EPOCH: 244224 # Wed May 24 2023 13:12:00 GMT+0000
INACTIVITY_SCORE_BIAS: 4
# 2**4 (= 16)
INACTIVITY_SCORE_RECOVERY_RATE: 16

View File

@@ -0,0 +1,8 @@
# EF
- enr:-Iq4QJk4WqRkjsX5c2CXtOra6HnxN-BMXnWhmhEQO9Bn9iABTJGdjUOurM7Btj1ouKaFkvTRoju5vz2GPmVON2dffQKGAX53x8JigmlkgnY0gmlwhLKAlv6Jc2VjcDI1NmsxoQK6S-Cii_KmfFdUJL2TANL3ksaKUnNXvTCv1tLwXs0QgIN1ZHCCIyk
- enr:-KG4QF6d6vMSboSujAXTI4vYqArccm0eIlXfcxf2Lx_VE1q6IkQo_2D5LAO3ZSBVUs0w5rrVDmABJZuMzISe_pZundADhGV0aDKQqX6DZjABcAAAAQAAAAAAAIJpZIJ2NIJpcISygIjpiXNlY3AyNTZrMaEDF3aSa7QSCvdqLpANNd8GML4PLEZVg45fKQwMWhDZjd2DdGNwgiMog3VkcIIjKA
- enr:-Ly4QJLXSSAj3ggPBIcodvBU6IyfpU_yW7E9J-5syoJorBuvcYj_Fokcjr303bQoTdWXADf8po0ssh75Mr5wVGzZZsMBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpCpfoNmMAFwAAABAAAAAAAAgmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQJrIlXIQDvQ6t9yDySqJYDXgZgLXzTvq8W7OI51jfmxJohzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA
# Teku
- enr:-LK4QMlzEff6d-M0A1pSFG5lJ2c56i_I-ZftdojZbW3ehkGNM4pkQuHQqzVvF1BG9aDjIakjnmO23mCBFFZ2w5zOsugEh2F0dG5ldHOIAAAAAAYAAACEZXRoMpCpfoNmMAFwAAABAAAAAAAAgmlkgnY0gmlwhKyuI_mJc2VjcDI1NmsxoQIH1kQRCZW-4AIVyAeXj5o49m_IqNFKRHp6tSpfXMUrSYN0Y3CCIyiDdWRwgiMo
# Sigma Prime
- enr:-Le4QI88slOwzz66Ksq8Vnz324DPb1BzSiY-WYPvnoJIl-lceW9bmSJnwDzgNbCjp5wsBigg76x4tValvGgQPxxSjrMBhGV0aDKQqX6DZjABcAAAAQAAAAAAAIJpZIJ2NIJpcIQ5gR6Wg2lwNpAgAUHQBwEQAAAAAAAAADR-iXNlY3AyNTZrMaEDPMSNdcL92uNIyCsS177Z6KTXlbZakQqxv3aQcWawNXeDdWRwgiMohHVkcDaCI4I

View File

@@ -0,0 +1,117 @@
# Extends the mainnet preset
PRESET_BASE: 'mainnet'
CONFIG_NAME: holesky
# Genesis
# ---------------------------------------------------------------
# `2**14` (= 16,384)
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 16384
# Sep-15-2023 13:55:00 +UTC
MIN_GENESIS_TIME: 1694786100
GENESIS_FORK_VERSION: 0x00017000
# Genesis delay 5 mins
GENESIS_DELAY: 300
# Forking
# ---------------------------------------------------------------
# Some forks are disabled for now:
# - These may be re-assigned to another fork-version later
# - Temporarily set to max uint64 value: 2**64 - 1
# Altair
ALTAIR_FORK_VERSION: 0x10017000
ALTAIR_FORK_EPOCH: 0
# Merge
BELLATRIX_FORK_VERSION: 0x20017000
BELLATRIX_FORK_EPOCH: 0
TERMINAL_TOTAL_DIFFICULTY: 0
TERMINAL_BLOCK_HASH: 0x0000000000000000000000000000000000000000000000000000000000000000
TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615
# Capella
CAPELLA_FORK_VERSION: 0x30017000
CAPELLA_FORK_EPOCH: 256
# DENEB
DENEB_FORK_VERSION: 0x40017000
DENEB_FORK_EPOCH: 18446744073709551615
# Time parameters
# ---------------------------------------------------------------
# 12 seconds
SECONDS_PER_SLOT: 12
# 14 (estimate from Eth1 mainnet)
SECONDS_PER_ETH1_BLOCK: 14
# 2**8 (= 256) epochs ~27 hours
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 256
# 2**8 (= 256) epochs ~27 hours
SHARD_COMMITTEE_PERIOD: 256
# 2**11 (= 2,048) Eth1 blocks ~8 hours
ETH1_FOLLOW_DISTANCE: 2048
# Validator cycle
# ---------------------------------------------------------------
# 2**2 (= 4)
INACTIVITY_SCORE_BIAS: 4
# 2**4 (= 16)
INACTIVITY_SCORE_RECOVERY_RATE: 16
# 28,000,000,000 Gwei to ensure quicker ejection
EJECTION_BALANCE: 28000000000
# 2**2 (= 4)
MIN_PER_EPOCH_CHURN_LIMIT: 4
# 2**16 (= 65,536)
CHURN_LIMIT_QUOTIENT: 65536
# Fork choice
# ---------------------------------------------------------------
# 40%
PROPOSER_SCORE_BOOST: 40
# Deposit contract
# ---------------------------------------------------------------
DEPOSIT_CHAIN_ID: 17000
DEPOSIT_NETWORK_ID: 17000
DEPOSIT_CONTRACT_ADDRESS: 0x4242424242424242424242424242424242424242
# Networking
# ---------------------------------------------------------------
# `10 * 2**20` (= 10485760, 10 MiB)
GOSSIP_MAX_SIZE: 10485760
# `2**10` (= 1024)
MAX_REQUEST_BLOCKS: 1024
# `2**8` (= 256)
EPOCHS_PER_SUBNET_SUBSCRIPTION: 256
# `MIN_VALIDATOR_WITHDRAWABILITY_DELAY + CHURN_LIMIT_QUOTIENT // 2` (= 33024, ~5 months)
MIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024
# `10 * 2**20` (=10485760, 10 MiB)
MAX_CHUNK_SIZE: 10485760
# 5s
TTFB_TIMEOUT: 5
# 10s
RESP_TIMEOUT: 10
ATTESTATION_PROPAGATION_SLOT_RANGE: 32
# 500ms
MAXIMUM_GOSSIP_CLOCK_DISPARITY: 500
MESSAGE_DOMAIN_INVALID_SNAPPY: 0x00000000
MESSAGE_DOMAIN_VALID_SNAPPY: 0x01000000
# 2 subnets per node
SUBNETS_PER_NODE: 2
# 2**8 (= 64)
ATTESTATION_SUBNET_COUNT: 64
ATTESTATION_SUBNET_EXTRA_BITS: 0
# ceillog2(ATTESTATION_SUBNET_COUNT) + ATTESTATION_SUBNET_EXTRA_BITS
ATTESTATION_SUBNET_PREFIX_BITS: 6
# Deneb
# `2**7` (=128)
MAX_REQUEST_BLOCKS_DENEB: 128
# MAX_REQUEST_BLOCKS_DENEB * MAX_BLOBS_PER_BLOCK
MAX_REQUEST_BLOB_SIDECARS: 768
# `2**12` (= 4096 epochs, ~18 days)
MIN_EPOCHS_FOR_BLOB_SIDECARS_REQUESTS: 4096
# `6`
BLOB_SIDECAR_SUBNET_COUNT: 6
# `uint64(6)`
MAX_BLOBS_PER_BLOCK: 6

View File

@@ -14,11 +14,20 @@
use discv5::enr::{CombinedKey, Enr};
use eth2_config::{instantiate_hardcoded_nets, HardcodedNet};
use kzg::{KzgPreset, KzgPresetId, TrustedSetup};
use pretty_reqwest_error::PrettyReqwestError;
use reqwest::blocking::Client;
use sensitive_url::SensitiveUrl;
use sha2::{Digest, Sha256};
use slog::{info, warn, Logger};
use std::fs::{create_dir_all, File};
use std::io::{Read, Write};
use std::path::PathBuf;
use std::str::FromStr;
use types::{BeaconState, ChainSpec, Config, Epoch, EthSpec, EthSpecId};
use std::time::Duration;
use types::{BeaconState, ChainSpec, Config, Epoch, EthSpec, EthSpecId, Hash256};
use url::Url;
pub use eth2_config::GenesisStateSource;
pub const DEPLOY_BLOCK_FILE: &str = "deploy_block.txt";
pub const BOOT_ENR_FILE: &str = "boot_enr.yaml";
@@ -70,6 +79,35 @@ fn get_trusted_setup_from_config(config: &Config) -> Result<Option<TrustedSetup>
.transpose()
}
/// A simple slice-or-vec enum to avoid cloning the beacon state bytes in the
/// binary whilst also supporting loading them from a file at runtime.
#[derive(Clone, PartialEq, Debug)]
pub enum GenesisStateBytes {
Slice(&'static [u8]),
Vec(Vec<u8>),
}
impl AsRef<[u8]> for GenesisStateBytes {
fn as_ref(&self) -> &[u8] {
match self {
GenesisStateBytes::Slice(slice) => slice,
GenesisStateBytes::Vec(vec) => vec.as_ref(),
}
}
}
impl From<&'static [u8]> for GenesisStateBytes {
fn from(slice: &'static [u8]) -> Self {
GenesisStateBytes::Slice(slice)
}
}
impl From<Vec<u8>> for GenesisStateBytes {
fn from(vec: Vec<u8>) -> Self {
GenesisStateBytes::Vec(vec)
}
}
/// Specifies an Eth2 network.
///
/// See the crate-level documentation for more details.
@@ -79,7 +117,8 @@ pub struct Eth2NetworkConfig {
/// value to be the block number where the first deposit occurs.
pub deposit_contract_deploy_block: u64,
pub boot_enr: Option<Vec<Enr<CombinedKey>>>,
pub genesis_state_bytes: Option<Vec<u8>>,
pub genesis_state_source: GenesisStateSource,
pub genesis_state_bytes: Option<GenesisStateBytes>,
pub config: Config,
pub kzg_trusted_setup: Option<TrustedSetup>,
}
@@ -107,8 +146,10 @@ impl Eth2NetworkConfig {
serde_yaml::from_reader(net.boot_enr)
.map_err(|e| format!("Unable to parse boot enr: {:?}", e))?,
),
genesis_state_bytes: Some(net.genesis_state_bytes.to_vec())
.filter(|bytes| !bytes.is_empty()),
genesis_state_source: net.genesis_state_source,
genesis_state_bytes: Some(net.genesis_state_bytes)
.filter(|bytes| !bytes.is_empty())
.map(Into::into),
config,
kzg_trusted_setup,
})
@@ -123,8 +164,37 @@ impl Eth2NetworkConfig {
}
/// Returns `true` if this configuration contains a `BeaconState`.
pub fn beacon_state_is_known(&self) -> bool {
self.genesis_state_bytes.is_some()
pub fn genesis_state_is_known(&self) -> bool {
self.genesis_state_source != GenesisStateSource::Unknown
}
/// The `genesis_validators_root` of the genesis state. May download the
/// genesis state if the value is not already available.
pub fn genesis_validators_root<E: EthSpec>(
&self,
genesis_state_url: Option<&str>,
timeout: Duration,
log: &Logger,
) -> Result<Option<Hash256>, String> {
if let GenesisStateSource::Url {
genesis_validators_root,
..
} = self.genesis_state_source
{
Hash256::from_str(genesis_validators_root)
.map(Option::Some)
.map_err(|e| {
format!(
"Unable to parse genesis state genesis_validators_root: {:?}",
e
)
})
} else {
self.genesis_state::<E>(genesis_state_url, timeout, log)?
.map(|state| state.genesis_validators_root())
.map(Result::Ok)
.transpose()
}
}
/// Construct a consolidated `ChainSpec` from the YAML config.
@@ -138,15 +208,65 @@ impl Eth2NetworkConfig {
}
/// Attempts to deserialize `self.beacon_state`, returning an error if it's missing or invalid.
pub fn beacon_state<E: EthSpec>(&self) -> Result<BeaconState<E>, String> {
///
/// If the genesis state is configured to be downloaded from a URL, then the
/// `genesis_state_url` will override the built-in list of download URLs.
pub fn genesis_state<E: EthSpec>(
&self,
genesis_state_url: Option<&str>,
timeout: Duration,
log: &Logger,
) -> Result<Option<BeaconState<E>>, String> {
let spec = self.chain_spec::<E>()?;
let genesis_state_bytes = self
.genesis_state_bytes
.as_ref()
.ok_or("Genesis state is unknown")?;
match &self.genesis_state_source {
GenesisStateSource::Unknown => Ok(None),
GenesisStateSource::IncludedBytes => {
let state = self
.genesis_state_bytes
.as_ref()
.map(|bytes| {
BeaconState::from_ssz_bytes(bytes.as_ref(), &spec).map_err(|e| {
format!("Built-in genesis state SSZ bytes are invalid: {:?}", e)
})
})
.ok_or("Genesis state bytes missing from Eth2NetworkConfig")??;
Ok(Some(state))
}
GenesisStateSource::Url {
urls: built_in_urls,
checksum,
genesis_validators_root,
} => {
let checksum = Hash256::from_str(checksum).map_err(|e| {
format!("Unable to parse genesis state bytes checksum: {:?}", e)
})?;
let bytes = if let Some(specified_url) = genesis_state_url {
download_genesis_state(&[specified_url], timeout, checksum, log)
} else {
download_genesis_state(built_in_urls, timeout, checksum, log)
}?;
let state = BeaconState::from_ssz_bytes(bytes.as_ref(), &spec).map_err(|e| {
format!("Downloaded genesis state SSZ bytes are invalid: {:?}", e)
})?;
BeaconState::from_ssz_bytes(genesis_state_bytes, &spec)
.map_err(|e| format!("Genesis state SSZ bytes are invalid: {:?}", e))
let genesis_validators_root =
Hash256::from_str(genesis_validators_root).map_err(|e| {
format!(
"Unable to parse genesis state genesis_validators_root: {:?}",
e
)
})?;
if state.genesis_validators_root() != genesis_validators_root {
return Err(format!(
"Downloaded genesis validators root {:?} does not match expected {:?}",
state.genesis_validators_root(),
genesis_validators_root
));
}
Ok(Some(state))
}
}
}
/// Write the files to the directory.
@@ -204,7 +324,7 @@ impl Eth2NetworkConfig {
File::create(&file)
.map_err(|e| format!("Unable to create {:?}: {:?}", file, e))
.and_then(|mut file| {
file.write_all(genesis_state_bytes)
file.write_all(genesis_state_bytes.as_ref())
.map_err(|e| format!("Unable to write {:?}: {:?}", file, e))
})?;
}
@@ -240,7 +360,7 @@ impl Eth2NetworkConfig {
// The genesis state is a special case because it uses SSZ, not YAML.
let genesis_file_path = base_dir.join(GENESIS_STATE_FILE);
let genesis_state_bytes = if genesis_file_path.exists() {
let (genesis_state_bytes, genesis_state_source) = if genesis_file_path.exists() {
let mut bytes = vec![];
File::open(&genesis_file_path)
.map_err(|e| format!("Unable to open {:?}: {:?}", genesis_file_path, e))
@@ -249,9 +369,15 @@ impl Eth2NetworkConfig {
.map_err(|e| format!("Unable to read {:?}: {:?}", file, e))
})?;
Some(bytes).filter(|bytes| !bytes.is_empty())
let state = Some(bytes).filter(|bytes| !bytes.is_empty());
let genesis_state_source = if state.is_some() {
GenesisStateSource::IncludedBytes
} else {
GenesisStateSource::Unknown
};
(state, genesis_state_source)
} else {
None
(None, GenesisStateSource::Unknown)
};
let kzg_trusted_setup = get_trusted_setup_from_config(&config)?;
@@ -259,13 +385,92 @@ impl Eth2NetworkConfig {
Ok(Self {
deposit_contract_deploy_block,
boot_enr,
genesis_state_bytes,
genesis_state_source,
genesis_state_bytes: genesis_state_bytes.map(Into::into),
config,
kzg_trusted_setup,
})
}
}
/// Try to download a genesis state from each of the `urls` in the order they
/// are defined. Return `Ok` if any url returns a response that matches the
/// given `checksum`.
fn download_genesis_state(
urls: &[&str],
timeout: Duration,
checksum: Hash256,
log: &Logger,
) -> Result<Vec<u8>, String> {
if urls.is_empty() {
return Err(
"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."
.to_string(),
);
}
let mut errors = vec![];
for url in urls {
// URLs are always expected to be the base URL of a server that supports
// the beacon-API.
let url = parse_state_download_url(url)?;
let redacted_url = SensitiveUrl::new(url.clone())
.map(|url| url.to_string())
.unwrap_or_else(|_| "<REDACTED>".to_string());
info!(
log,
"Downloading genesis state";
"server" => &redacted_url,
"timeout" => ?timeout,
"info" => "this may take some time on testnets with large validator counts"
);
let client = Client::new();
let response = client
.get(url)
.header("Accept", "application/octet-stream")
.timeout(timeout)
.send()
.and_then(|r| r.error_for_status().and_then(|r| r.bytes()));
match response {
Ok(bytes) => {
// Check the server response against our local checksum.
if Sha256::digest(bytes.as_ref())[..] == checksum[..] {
return Ok(bytes.into());
} else {
warn!(
log,
"Genesis state download failed";
"server" => &redacted_url,
"timeout" => ?timeout,
);
errors.push(format!(
"Response from {} did not match local checksum",
redacted_url
))
}
}
Err(e) => errors.push(PrettyReqwestError::from(e).to_string()),
}
}
Err(format!(
"Unable to download a genesis state from {} source(s): {}",
errors.len(),
errors.join(",")
))
}
/// Parses the `url` and joins the necessary state download path.
fn parse_state_download_url(url: &str) -> Result<Url, String> {
Url::parse(url)
.map_err(|e| format!("Invalid genesis state URL: {:?}", e))?
.join("eth/v2/debug/beacon/states/genesis")
.map_err(|e| format!("Failed to append genesis state path to URL: {:?}", e))
}
#[cfg(test)]
mod tests {
use super::*;
@@ -305,7 +510,9 @@ mod tests {
#[test]
fn mainnet_genesis_state() {
let config = Eth2NetworkConfig::from_hardcoded_net(&MAINNET).unwrap();
config.beacon_state::<E>().expect("beacon state can decode");
config
.genesis_state::<E>(None, Duration::from_secs(1), &logging::test_logger())
.expect("beacon state can decode");
}
#[test]
@@ -319,10 +526,10 @@ mod tests {
fn hard_coded_nets_work() {
for net in HARDCODED_NETS {
let config = Eth2NetworkConfig::from_hardcoded_net(net)
.unwrap_or_else(|_| panic!("{:?}", net.name));
.unwrap_or_else(|e| panic!("{:?}: {:?}", net.name, e));
// Ensure we can parse the YAML config to a chain spec.
if net.name == types::GNOSIS {
if config.config.preset_base == types::GNOSIS {
config.chain_spec::<GnosisEthSpec>().unwrap();
} else {
config.chain_spec::<MainnetEthSpec>().unwrap();
@@ -330,10 +537,25 @@ mod tests {
assert_eq!(
config.genesis_state_bytes.is_some(),
net.genesis_is_known,
net.genesis_state_source == GenesisStateSource::IncludedBytes,
"{:?}",
net.name
);
if let GenesisStateSource::Url {
urls,
checksum,
genesis_validators_root,
} = net.genesis_state_source
{
Hash256::from_str(checksum).expect("the checksum must be a valid 32-byte value");
Hash256::from_str(genesis_validators_root)
.expect("the GVR must be a valid 32-byte value");
for url in urls {
parse_state_download_url(url).expect("url must be valid");
}
}
assert_eq!(config.config.config_name, Some(net.config_dir.to_string()));
}
}
@@ -369,10 +591,20 @@ mod tests {
let base_dir = temp_dir.path().join("my_testnet");
let deposit_contract_deploy_block = 42;
let genesis_state_source = if genesis_state.is_some() {
GenesisStateSource::IncludedBytes
} else {
GenesisStateSource::Unknown
};
let testnet = Eth2NetworkConfig {
deposit_contract_deploy_block,
boot_enr,
genesis_state_bytes: genesis_state.as_ref().map(Encode::as_ssz_bytes),
genesis_state_source,
genesis_state_bytes: genesis_state
.as_ref()
.map(Encode::as_ssz_bytes)
.map(Into::into),
config,
kzg_trusted_setup: None,
};