Add support for multiple testnet flags (#1396)
## Issue Addressed NA ## Proposed Changes Allows for multiple "hardcoded" testnets. ## Additional Info This PR is incomplete. ## TODO - [x] Add flag to CLI, integrate with rest of Lighthouse. Co-authored-by: Pawan Dhananjay <pawandhananjay@gmail.com> Co-authored-by: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
parent
395d99ce03
commit
36d3d37cb4
72
Cargo.lock
generated
72
Cargo.lock
generated
@ -1604,9 +1604,12 @@ name = "eth2_testnet_config"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"enr",
|
||||
"eth2_config",
|
||||
"eth2_ssz",
|
||||
"handlebars",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_yaml",
|
||||
"tempdir",
|
||||
"types",
|
||||
@ -2116,6 +2119,20 @@ version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d36fab90f82edc3c747f9d438e06cf0a491055896f2a279638bb5beed6c40177"
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "3.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86dbc8a0746b08f363d2e00da48e6c9ceb75c198ac692d2715fcbb5bee74c87d"
|
||||
dependencies = [
|
||||
"log 0.4.11",
|
||||
"pest",
|
||||
"pest_derive",
|
||||
"quick-error",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.6.3"
|
||||
@ -3076,6 +3093,12 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maplit"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
|
||||
|
||||
[[package]]
|
||||
name = "matches"
|
||||
version = "0.1.8"
|
||||
@ -3706,6 +3729,49 @@ version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
|
||||
|
||||
[[package]]
|
||||
name = "pest"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||
dependencies = [
|
||||
"ucd-trie",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_derive"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "833d1ae558dc601e9a60366421196a8d94bc0ac980476d0b67e1d0988d72b2d0"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_generator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_generator"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99b8db626e31e5b81787b9783425769681b347011cc59471e33ea46d2ea0cf55"
|
||||
dependencies = [
|
||||
"pest",
|
||||
"pest_meta",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pest_meta"
|
||||
version = "2.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54be6e404f5317079812fc8f9f5279de376d8856929e21c184ecf6bbd692a11d"
|
||||
dependencies = [
|
||||
"maplit",
|
||||
"pest",
|
||||
"sha-1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.5.1"
|
||||
@ -5883,6 +5949,12 @@ dependencies = [
|
||||
"tree_hash_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ucd-trie"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||
|
||||
[[package]]
|
||||
name = "uhttp_sse"
|
||||
version = "0.5.1"
|
||||
|
@ -46,7 +46,6 @@ pub struct Config {
|
||||
pub db_name: String,
|
||||
/// Path where the freezer database will be located.
|
||||
pub freezer_db_path: Option<PathBuf>,
|
||||
pub testnet_dir: Option<PathBuf>,
|
||||
pub log_file: PathBuf,
|
||||
pub spec_constants: String,
|
||||
/// If true, the node will use co-ordinated junk for eth1 values.
|
||||
@ -75,7 +74,6 @@ impl Default for Config {
|
||||
data_dir: PathBuf::from(DEFAULT_DATADIR),
|
||||
db_name: "chain_db".to_string(),
|
||||
freezer_db_path: None,
|
||||
testnet_dir: None,
|
||||
log_file: PathBuf::from(""),
|
||||
genesis: <_>::default(),
|
||||
store: <_>::default(),
|
||||
|
@ -70,7 +70,6 @@ pub fn get_config<E: EthSpec>(
|
||||
info!(log, "Data directory initialised"; "datadir" => log_dir.into_os_string().into_string().expect("Datadir should be a valid os string"));
|
||||
|
||||
client_config.spec_constants = spec_constants.into();
|
||||
client_config.testnet_dir = get_testnet_dir(cli_args);
|
||||
|
||||
/*
|
||||
* Networking
|
||||
@ -326,8 +325,7 @@ pub fn get_config<E: EthSpec>(
|
||||
/*
|
||||
* Load the eth2 testnet dir to obtain some additional config values.
|
||||
*/
|
||||
let eth2_testnet_config: Eth2TestnetConfig<E> =
|
||||
get_eth2_testnet_config(&client_config.testnet_dir)?;
|
||||
let eth2_testnet_config: Eth2TestnetConfig<E> = get_eth2_testnet_config(&cli_args)?;
|
||||
|
||||
client_config.eth1.deposit_contract_address =
|
||||
format!("{:?}", eth2_testnet_config.deposit_contract_address()?);
|
||||
@ -384,29 +382,19 @@ pub fn get_data_dir(cli_args: &ArgMatches) -> PathBuf {
|
||||
.unwrap_or_else(|| PathBuf::from("."))
|
||||
}
|
||||
|
||||
/// Gets the testnet dir which should be used.
|
||||
pub fn get_testnet_dir(cli_args: &ArgMatches) -> Option<PathBuf> {
|
||||
// Read the `--testnet-dir` flag.
|
||||
if let Some(val) = cli_args.value_of("testnet-dir") {
|
||||
Some(PathBuf::from(val))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// If `testnet_dir` is `Some`, returns the `Eth2TestnetConfig` at that path or returns an error.
|
||||
/// If it is `None`, returns the "hard coded" config.
|
||||
/// Try to parse the eth2 testnet config from the `testnet`, `testnet-dir` flags in that order.
|
||||
/// Returns the default hardcoded testnet if neither flags are set.
|
||||
pub fn get_eth2_testnet_config<E: EthSpec>(
|
||||
testnet_dir: &Option<PathBuf>,
|
||||
cli_args: &ArgMatches,
|
||||
) -> Result<Eth2TestnetConfig<E>, String> {
|
||||
if let Some(testnet_dir) = testnet_dir {
|
||||
Eth2TestnetConfig::load(testnet_dir.clone())
|
||||
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", testnet_dir, e))
|
||||
let optional_testnet_config = if cli_args.is_present("testnet") {
|
||||
clap_utils::parse_hardcoded_network(cli_args, "testnet")?
|
||||
} else if cli_args.is_present("testnet-dir") {
|
||||
clap_utils::parse_testnet_dir(cli_args, "testnet-dir")?
|
||||
} else {
|
||||
Eth2TestnetConfig::hard_coded()
|
||||
.map_err(|e| format!("Error parsing hardcoded testnet: {}", e))?
|
||||
.ok_or_else(|| BAD_TESTNET_DIR_MESSAGE.to_string())
|
||||
}
|
||||
Eth2TestnetConfig::hard_coded_default()?
|
||||
};
|
||||
optional_testnet_config.ok_or_else(|| BAD_TESTNET_DIR_MESSAGE.to_string())
|
||||
}
|
||||
|
||||
/// A bit of hack to find an unused port.
|
||||
|
@ -7,7 +7,7 @@ mod config;
|
||||
pub use beacon_chain;
|
||||
pub use cli::cli_app;
|
||||
pub use client::{Client, ClientBuilder, ClientConfig, ClientGenesis};
|
||||
pub use config::{get_data_dir, get_eth2_testnet_config, get_testnet_dir};
|
||||
pub use config::{get_data_dir, get_eth2_testnet_config};
|
||||
pub use eth2_config::Eth2Config;
|
||||
|
||||
use beacon_chain::events::TeeEventHandler;
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Summary
|
||||
|
||||
* [Introduction](./intro.md)
|
||||
* [Become an Altona Validator](./become-a-validator.md)
|
||||
* [Become a Medalla Validator](./become-a-validator.md)
|
||||
* [Using Docker](./become-a-validator-docker.md)
|
||||
* [Building from Source](./become-a-validator-source.md)
|
||||
* [Installation](./installation.md)
|
||||
|
@ -34,7 +34,7 @@ If you're using a Mac, follow the instructions [listed here](https://github.com/
|
||||
Once you have geth installed, use this command to start your Eth1 node:
|
||||
|
||||
```bash
|
||||
geth --goerli --rpc
|
||||
geth --goerli --http
|
||||
```
|
||||
|
||||
## 3. Start your beacon node
|
||||
|
@ -1,18 +1,18 @@
|
||||
# Become an Ethereum 2.0 Testnet Validator on Altona
|
||||
# Become an Ethereum 2.0 Testnet Validator on Medalla
|
||||
|
||||
Running a Lighthouse validator on the [Altona](https://github.com/goerli/altona)
|
||||
Running a Lighthouse validator on the [Medalla](https://github.com/goerli/medalla/tree/master/medalla)
|
||||
multi-client testnet is easy if you're familiar with the terminal.
|
||||
|
||||
Lighthouse runs on Linux, MacOS and Windows and has a Docker work-flow to make
|
||||
things as simple as possible.
|
||||
|
||||
## 0. Acquire Goerli ETH
|
||||
Before you install Lighthouse, you'll need [Metamask](https://metamask.io/) and 3.2 gETH
|
||||
Before you install Lighthouse, you'll need [Metamask](https://metamask.io/) and 32 gETH
|
||||
(Goerli ETH). We recommend the [mudit.blog
|
||||
faucet](https://faucet.goerli.mudit.blog/) for those familiar with Goerli, or
|
||||
[goerli.net](https://goerli.net/) for an overview of the testnet.
|
||||
|
||||
> If this is your first time using Metamask and/or interacting with an ethereum test network, we recommend going through the beginning of [this guide](https://hack.aragon.org/docs/guides-use-metamask) first (up to the *Signing your first transaction with MetaMask* section).
|
||||
> If this is your first time using Metamask and/or interacting with an Ethereum test network, we recommend going through the beginning of [this guide](https://hack.aragon.org/docs/guides-use-metamask) first (up to the *Signing your first transaction with MetaMask* section).
|
||||
|
||||
## 1. Install and start Lighthouse
|
||||
|
||||
@ -29,7 +29,7 @@ Once you've completed **either one** of these steps, you can move onto the next
|
||||
|
||||
<div class="form-signin" id="uploadDiv">
|
||||
<p>Upload the <code>eth1_deposit_data.rlp</code> file from your validator
|
||||
directory (created in the previous step) to submit your 3.2 Goerli-ETH
|
||||
directory (created in the previous step) to submit your 32 Goerli-ETH
|
||||
deposit using Metamask.</p>
|
||||
<p>Note that the method you used in step 1 will determine where this file is
|
||||
located.</p>
|
||||
|
@ -7,7 +7,7 @@ client to connect to the beacon node and produce blocks and attestations.
|
||||
|
||||
HTTP Path | HTTP Method | Description |
|
||||
| - | - | ---- |
|
||||
[`/validator/duties`](#validatorduties) | GET | Provides block and attestation production information for validators.
|
||||
[`/validator/duties`](#validatorduties) | POST | Provides block and attestation production information for validators.
|
||||
[`/validator/subscribe`](#validatorsubscribe) | POST | Subscribes a list of validators to the beacon node for a particular duty/slot.
|
||||
[`/validator/duties/all`](#validatordutiesall) | GET |Provides block and attestation production information for all validators.
|
||||
[`/validator/duties/active`](#validatordutiesactive) | GET | Provides block and attestation production information for all active validators.
|
||||
|
@ -1,6 +1,6 @@
|
||||
const NETWORK = "5";
|
||||
const NETWORK_NAME = "Goerli Test Network";
|
||||
const DEPOSIT_CONTRACT = "0x16e82D77882A663454Ef92806b7DeCa1D394810f";
|
||||
const DEPOSIT_CONTRACT = "0x07b39F4fDE4A38bACe212b546dAc87C58DfE3fDC";
|
||||
const DEPOSIT_AMOUNT_ETH = "32";
|
||||
const GAS_LIMIT = "4000000";
|
||||
const DEPOSIT_DATA_BYTES = 420;
|
||||
|
@ -14,20 +14,24 @@ pub const BAD_TESTNET_DIR_MESSAGE: &str = "The hard-coded testnet directory was
|
||||
|
||||
/// Attempts to load the testnet dir at the path if `name` is in `matches`, returning an error if
|
||||
/// the path cannot be found or the testnet dir is invalid.
|
||||
///
|
||||
/// If `name` is not in `matches`, attempts to return the "hard coded" testnet dir.
|
||||
pub fn parse_testnet_dir_with_hardcoded_default<E: EthSpec>(
|
||||
pub fn parse_testnet_dir<E: EthSpec>(
|
||||
matches: &ArgMatches,
|
||||
name: &'static str,
|
||||
) -> Result<Option<Eth2TestnetConfig<E>>, String> {
|
||||
if let Some(path) = parse_optional::<PathBuf>(matches, name)? {
|
||||
Eth2TestnetConfig::load(path.clone())
|
||||
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", path, e))
|
||||
.map(Some)
|
||||
} else {
|
||||
Eth2TestnetConfig::hard_coded()
|
||||
.map_err(|e| format!("{} Error : {}", BAD_TESTNET_DIR_MESSAGE, e))
|
||||
}
|
||||
let path = parse_required::<PathBuf>(matches, name)?;
|
||||
Eth2TestnetConfig::load(path.clone())
|
||||
.map_err(|e| format!("Unable to open testnet dir at {:?}: {}", path, e))
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
/// Attempts to load a hardcoded network config if `name` is in `matches`, returning an error if
|
||||
/// the name is not a valid network name.
|
||||
pub fn parse_hardcoded_network<E: EthSpec>(
|
||||
matches: &ArgMatches,
|
||||
name: &str,
|
||||
) -> Result<Option<Eth2TestnetConfig<E>>, String> {
|
||||
let network_name = parse_required::<String>(matches, name)?;
|
||||
Eth2TestnetConfig::constant(network_name.as_str())
|
||||
}
|
||||
|
||||
/// If `name` is in `matches`, parses the value as a path. Otherwise, attempts to find the user's
|
||||
@ -52,7 +56,7 @@ pub fn parse_path_with_default_in_home_dir(
|
||||
|
||||
/// Returns the value of `name` or an error if it is not in `matches` or does not parse
|
||||
/// successfully using `std::string::FromStr`.
|
||||
pub fn parse_required<T>(matches: &ArgMatches, name: &'static str) -> Result<T, String>
|
||||
pub fn parse_required<T>(matches: &ArgMatches, name: &str) -> Result<T, String>
|
||||
where
|
||||
T: FromStr,
|
||||
<T as FromStr>::Err: std::fmt::Display,
|
||||
@ -62,7 +66,7 @@ where
|
||||
|
||||
/// Returns the value of `name` (if present) or an error if it does not parse successfully using
|
||||
/// `std::string::FromStr`.
|
||||
pub fn parse_optional<T>(matches: &ArgMatches, name: &'static str) -> Result<Option<T>, String>
|
||||
pub fn parse_optional<T>(matches: &ArgMatches, name: &str) -> Result<Option<T>, String>
|
||||
where
|
||||
T: FromStr,
|
||||
<T as FromStr>::Err: std::fmt::Display,
|
||||
|
@ -1,4 +1,6 @@
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
use types::ChainSpec;
|
||||
|
||||
/// The core configuration of a Lighthouse beacon node.
|
||||
@ -41,6 +43,86 @@ impl Eth2Config {
|
||||
}
|
||||
}
|
||||
|
||||
/// A directory that can be built by downloading files via HTTP.
|
||||
///
|
||||
/// Used by the `eth2_testnet_config` crate to initialize testnet directories during build and
|
||||
/// access them at runtime.
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Eth2NetDirectory<'a> {
|
||||
pub name: &'a str,
|
||||
pub unique_id: &'a str,
|
||||
pub commit: &'a str,
|
||||
pub url_template: &'a str,
|
||||
pub genesis_is_known: bool,
|
||||
}
|
||||
|
||||
impl<'a> Eth2NetDirectory<'a> {
|
||||
/// The directory that should be used to store files downloaded for this net.
|
||||
pub fn dir(&self) -> PathBuf {
|
||||
env::var("CARGO_MANIFEST_DIR")
|
||||
.expect("should know manifest dir")
|
||||
.parse::<PathBuf>()
|
||||
.expect("should parse manifest dir as path")
|
||||
.join(self.unique_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! unique_id {
|
||||
($name: tt, $commit: tt, $genesis_is_known: tt) => {
|
||||
concat!("testnet_", $name, "_", $commit, "_", $genesis_is_known);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! define_net {
|
||||
($title: ident, $macro_title: tt, $name: tt, $commit: tt, $url_template: tt, $genesis_is_known: tt) => {
|
||||
#[macro_use]
|
||||
pub mod $title {
|
||||
use super::*;
|
||||
|
||||
pub const ETH2_NET_DIR: Eth2NetDirectory = Eth2NetDirectory {
|
||||
name: $name,
|
||||
unique_id: unique_id!($name, $commit, $genesis_is_known),
|
||||
commit: $commit,
|
||||
url_template: $url_template,
|
||||
genesis_is_known: $genesis_is_known,
|
||||
};
|
||||
|
||||
// A wrapper around `std::include_bytes` which includes a file from a specific testnet
|
||||
// directory. Used by upstream crates to import files at compile time.
|
||||
#[macro_export]
|
||||
macro_rules! $macro_title {
|
||||
($base_dir: tt, $filename: tt) => {
|
||||
include_bytes!(concat!(
|
||||
$base_dir,
|
||||
unique_id!($name, $commit, $genesis_is_known),
|
||||
"/",
|
||||
$filename
|
||||
))
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
define_net!(
|
||||
altona,
|
||||
include_altona_file,
|
||||
"altona",
|
||||
"a94e00c1a03df851f960fcf44a79f2a6b1d29af1",
|
||||
"https://raw.githubusercontent.com/sigp/witti/{{ commit }}/altona/lighthouse/{{ file }}",
|
||||
true
|
||||
);
|
||||
|
||||
define_net!(
|
||||
medalla,
|
||||
include_medalla_file,
|
||||
"medalla",
|
||||
"b21fef76ddf472c6cea62d5c98b678033a9b195a",
|
||||
"https://raw.githubusercontent.com/sigp/witti/{{ commit }}/medalla/{{ file }}",
|
||||
false
|
||||
);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
@ -8,6 +8,9 @@ build = "build.rs"
|
||||
|
||||
[build-dependencies]
|
||||
reqwest = { version = "0.10.4", features = ["blocking"] }
|
||||
eth2_config = { path = "../eth2_config"}
|
||||
handlebars = "3.3.0"
|
||||
serde_json = "1.0.56"
|
||||
|
||||
[dev-dependencies]
|
||||
tempdir = "0.3.7"
|
||||
@ -18,3 +21,4 @@ serde_yaml = "0.8.11"
|
||||
types = { path = "../../consensus/types"}
|
||||
enr = { version = "0.1.0", features = ["libsecp256k1", "ed25519"] }
|
||||
eth2_ssz = "0.1.2"
|
||||
eth2_config = { path = "../eth2_config"}
|
||||
|
@ -1,48 +1,61 @@
|
||||
//! Downloads a testnet configuration from Github.
|
||||
|
||||
use std::env;
|
||||
use eth2_config::{altona, medalla, Eth2NetDirectory};
|
||||
use handlebars::Handlebars;
|
||||
use serde_json::json;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
const TESTNET_ID: &str = "altona-v3";
|
||||
const ETH2_NET_DIRS: &[Eth2NetDirectory<'static>] = &[altona::ETH2_NET_DIR, medalla::ETH2_NET_DIR];
|
||||
|
||||
fn main() {
|
||||
if !base_dir().exists() {
|
||||
std::fs::create_dir_all(base_dir())
|
||||
.unwrap_or_else(|_| panic!("Unable to create {:?}", base_dir()));
|
||||
for testnet in ETH2_NET_DIRS {
|
||||
let testnet_dir = testnet.dir();
|
||||
|
||||
match get_all_files() {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
std::fs::remove_dir_all(base_dir()).unwrap_or_else(|_| panic!(
|
||||
"{}. Failed to remove {:?}, please remove the directory manually because it may contains incomplete testnet data.",
|
||||
e,
|
||||
base_dir(),
|
||||
));
|
||||
panic!(e);
|
||||
if !testnet_dir.exists() {
|
||||
std::fs::create_dir_all(&testnet_dir)
|
||||
.unwrap_or_else(|_| panic!("Unable to create {:?}", testnet_dir));
|
||||
|
||||
match get_all_files(testnet) {
|
||||
Ok(()) => (),
|
||||
Err(e) => {
|
||||
std::fs::remove_dir_all(&testnet_dir).unwrap_or_else(|_| panic!(
|
||||
"{}. Failed to remove {:?}, please remove the directory manually because it may contains incomplete testnet data.",
|
||||
e,
|
||||
testnet_dir,
|
||||
));
|
||||
panic!(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_all_files() -> Result<(), String> {
|
||||
get_file("boot_enr.yaml")?;
|
||||
get_file("config.yaml")?;
|
||||
get_file("deploy_block.txt")?;
|
||||
get_file("deposit_contract.txt")?;
|
||||
get_file("genesis.ssz")?;
|
||||
fn get_all_files(testnet: &Eth2NetDirectory<'static>) -> Result<(), String> {
|
||||
get_file(testnet, "boot_enr.yaml")?;
|
||||
get_file(testnet, "config.yaml")?;
|
||||
get_file(testnet, "deploy_block.txt")?;
|
||||
get_file(testnet, "deposit_contract.txt")?;
|
||||
|
||||
if testnet.genesis_is_known {
|
||||
get_file(testnet, "genesis.ssz")?;
|
||||
} else {
|
||||
File::create(testnet.dir().join("genesis.ssz")).unwrap();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_file(filename: &str) -> Result<(), String> {
|
||||
let url = format!(
|
||||
"https://raw.githubusercontent.com/sigp/witti/a94e00c1a03df851f960fcf44a79f2a6b1d29af1/altona/lighthouse/{}",
|
||||
filename
|
||||
);
|
||||
fn get_file(testnet: &Eth2NetDirectory, filename: &str) -> Result<(), String> {
|
||||
let url = Handlebars::new()
|
||||
.render_template(
|
||||
testnet.url_template,
|
||||
&json!({"commit": testnet.commit, "file": filename}),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let path = testnet.dir().join(filename);
|
||||
|
||||
let path = base_dir().join(filename);
|
||||
let mut file =
|
||||
File::create(path).map_err(|e| format!("Failed to create {}: {:?}", filename, e))?;
|
||||
|
||||
@ -65,11 +78,3 @@ pub fn get_file(filename: &str) -> Result<(), String> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn base_dir() -> PathBuf {
|
||||
env::var("CARGO_MANIFEST_DIR")
|
||||
.expect("should know manifest dir")
|
||||
.parse::<PathBuf>()
|
||||
.expect("should parse manifest dir as path")
|
||||
.join(TESTNET_ID)
|
||||
}
|
||||
|
@ -6,6 +6,8 @@
|
||||
//! others. We are unable to conform to the repo until we have the following PR merged:
|
||||
//!
|
||||
//! https://github.com/sigp/lighthouse/pull/605
|
||||
//!
|
||||
use eth2_config::{include_altona_file, include_medalla_file, unique_id};
|
||||
|
||||
use enr::{CombinedKey, Enr};
|
||||
use ssz::{Decode, Encode};
|
||||
@ -20,16 +22,40 @@ pub const BOOT_ENR_FILE: &str = "boot_enr.yaml";
|
||||
pub const GENESIS_STATE_FILE: &str = "genesis.ssz";
|
||||
pub const YAML_CONFIG_FILE: &str = "config.yaml";
|
||||
|
||||
/// The name of the testnet to hardcode.
|
||||
///
|
||||
/// Should be set to `None` when no existing testnet is compatible with the codebase.
|
||||
pub const HARDCODED_TESTNET: Option<&str> = Some("altona-v3");
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct HardcodedNet {
|
||||
pub unique_id: &'static str,
|
||||
pub name: &'static str,
|
||||
pub genesis_is_known: bool,
|
||||
pub yaml_config: &'static [u8],
|
||||
pub deploy_block: &'static [u8],
|
||||
pub boot_enr: &'static [u8],
|
||||
pub deposit_contract_address: &'static [u8],
|
||||
pub genesis_state: &'static [u8],
|
||||
}
|
||||
|
||||
pub const HARDCODED_YAML_CONFIG: &[u8] = include_bytes!("../altona-v3/config.yaml");
|
||||
pub const HARDCODED_DEPLOY_BLOCK: &[u8] = include_bytes!("../altona-v3/deploy_block.txt");
|
||||
pub const HARDCODED_DEPOSIT_CONTRACT: &[u8] = include_bytes!("../altona-v3/deposit_contract.txt");
|
||||
pub const HARDCODED_GENESIS_STATE: &[u8] = include_bytes!("../altona-v3/genesis.ssz");
|
||||
pub const HARDCODED_BOOT_ENR: &[u8] = include_bytes!("../altona-v3/boot_enr.yaml");
|
||||
macro_rules! define_net {
|
||||
($mod: ident, $include_file: tt) => {{
|
||||
use eth2_config::$mod::ETH2_NET_DIR;
|
||||
|
||||
HardcodedNet {
|
||||
unique_id: ETH2_NET_DIR.unique_id,
|
||||
name: ETH2_NET_DIR.name,
|
||||
genesis_is_known: ETH2_NET_DIR.genesis_is_known,
|
||||
yaml_config: $include_file!("../", "config.yaml"),
|
||||
deploy_block: $include_file!("../", "deploy_block.txt"),
|
||||
boot_enr: $include_file!("../", "boot_enr.yaml"),
|
||||
deposit_contract_address: $include_file!("../", "deposit_contract.txt"),
|
||||
genesis_state: $include_file!("../", "genesis.ssz"),
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
const ALTONA: HardcodedNet = define_net!(altona, include_altona_file);
|
||||
const MEDALLA: HardcodedNet = define_net!(medalla, include_medalla_file);
|
||||
|
||||
const HARDCODED_NETS: &[HardcodedNet] = &[ALTONA, MEDALLA];
|
||||
pub const DEFAULT_HARDCODED_TESTNET: &str = "medalla";
|
||||
|
||||
/// Specifies an Eth2 testnet.
|
||||
///
|
||||
@ -44,34 +70,46 @@ pub struct Eth2TestnetConfig<E: EthSpec> {
|
||||
}
|
||||
|
||||
impl<E: EthSpec> Eth2TestnetConfig<E> {
|
||||
/// Creates the `Eth2TestnetConfig` that was included in the binary at compile time. This can be
|
||||
/// considered the default Lighthouse testnet.
|
||||
///
|
||||
/// Returns an error if those included bytes are invalid (this is unlikely).
|
||||
/// Returns `None` if the hardcoded testnet is disabled.
|
||||
pub fn hard_coded() -> Result<Option<Self>, String> {
|
||||
if HARDCODED_TESTNET.is_some() {
|
||||
Ok(Some(Self {
|
||||
deposit_contract_address: serde_yaml::from_reader(HARDCODED_DEPOSIT_CONTRACT)
|
||||
.map_err(|e| format!("Unable to parse contract address: {:?}", e))?,
|
||||
deposit_contract_deploy_block: serde_yaml::from_reader(HARDCODED_DEPLOY_BLOCK)
|
||||
.map_err(|e| format!("Unable to parse deploy block: {:?}", e))?,
|
||||
boot_enr: Some(
|
||||
serde_yaml::from_reader(HARDCODED_BOOT_ENR)
|
||||
.map_err(|e| format!("Unable to parse boot enr: {:?}", e))?,
|
||||
),
|
||||
genesis_state: Some(
|
||||
BeaconState::from_ssz_bytes(HARDCODED_GENESIS_STATE)
|
||||
.map_err(|e| format!("Unable to parse genesis state: {:?}", e))?,
|
||||
),
|
||||
yaml_config: Some(
|
||||
serde_yaml::from_reader(HARDCODED_YAML_CONFIG)
|
||||
.map_err(|e| format!("Unable to parse genesis state: {:?}", e))?,
|
||||
),
|
||||
}))
|
||||
/// Returns the default hard coded testnet.
|
||||
pub fn hard_coded_default() -> Result<Option<Self>, String> {
|
||||
Self::constant(DEFAULT_HARDCODED_TESTNET)
|
||||
}
|
||||
/// When Lighthouse is built it includes zero or more "hardcoded" network specifications. This
|
||||
/// function allows for instantiating one of these nets by name.
|
||||
pub fn constant(name: &str) -> Result<Option<Self>, String> {
|
||||
HARDCODED_NETS
|
||||
.iter()
|
||||
.find(|net| net.name == name)
|
||||
.map(Self::from_hardcoded_net)
|
||||
.transpose()
|
||||
}
|
||||
|
||||
/// Instantiates `Self` from a `HardcodedNet`.
|
||||
fn from_hardcoded_net(net: &HardcodedNet) -> Result<Self, String> {
|
||||
let genesis_state = if net.genesis_state.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
Some(
|
||||
BeaconState::from_ssz_bytes(net.genesis_state)
|
||||
.map_err(|e| format!("Unable to parse genesis state: {:?}", e))?,
|
||||
)
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
deposit_contract_address: serde_yaml::from_reader(net.deposit_contract_address)
|
||||
.map_err(|e| format!("Unable to parse contract address: {:?}", e))?,
|
||||
deposit_contract_deploy_block: serde_yaml::from_reader(net.deploy_block)
|
||||
.map_err(|e| format!("Unable to parse deploy block: {:?}", e))?,
|
||||
boot_enr: Some(
|
||||
serde_yaml::from_reader(net.boot_enr)
|
||||
.map_err(|e| format!("Unable to parse boot enr: {:?}", e))?,
|
||||
),
|
||||
genesis_state,
|
||||
yaml_config: Some(
|
||||
serde_yaml::from_reader(net.yaml_config)
|
||||
.map_err(|e| format!("Unable to parse yaml config: {:?}", e))?,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
// Write the files to the directory.
|
||||
@ -215,13 +253,10 @@ mod tests {
|
||||
type E = MainnetEthSpec;
|
||||
|
||||
#[test]
|
||||
fn hard_coded_works() {
|
||||
if let Some(dir) =
|
||||
Eth2TestnetConfig::<E>::hard_coded().expect("should decode hard_coded params")
|
||||
{
|
||||
assert!(dir.boot_enr.is_some());
|
||||
assert!(dir.genesis_state.is_some());
|
||||
assert!(dir.yaml_config.is_some());
|
||||
fn hard_coded_nets_work() {
|
||||
for net in HARDCODED_NETS {
|
||||
let config = Eth2TestnetConfig::<E>::from_hardcoded_net(net).unwrap();
|
||||
assert_eq!(config.genesis_state.is_some(), net.genesis_is_known);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +14,7 @@ fn builder() -> EnvironmentBuilder<MainnetEthSpec> {
|
||||
}
|
||||
|
||||
fn eth2_testnet_config() -> Option<Eth2TestnetConfig<MainnetEthSpec>> {
|
||||
Eth2TestnetConfig::hard_coded().expect("should decode hard_coded params")
|
||||
Eth2TestnetConfig::hard_coded_default().expect("should decode hard_coded params")
|
||||
}
|
||||
|
||||
mod setup_eth2_config {
|
||||
|
@ -5,7 +5,7 @@ use beacon_node::ProductionBeaconNode;
|
||||
use clap::{App, Arg, ArgMatches};
|
||||
use env_logger::{Builder, Env};
|
||||
use environment::EnvironmentBuilder;
|
||||
use eth2_testnet_config::HARDCODED_TESTNET;
|
||||
use eth2_testnet_config::{Eth2TestnetConfig, DEFAULT_HARDCODED_TESTNET};
|
||||
use git_version::git_version;
|
||||
use slog::{crit, info, warn};
|
||||
use std::path::PathBuf;
|
||||
@ -98,6 +98,17 @@ fn main() {
|
||||
.takes_value(true)
|
||||
.global(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("testnet")
|
||||
.long("testnet")
|
||||
.value_name("testnet")
|
||||
.help("Name of network lighthouse will connect to")
|
||||
.possible_values(&["medalla", "altona"])
|
||||
.conflicts_with("testnet-dir")
|
||||
.takes_value(true)
|
||||
.global(true)
|
||||
|
||||
)
|
||||
.subcommand(beacon_node::cli_app())
|
||||
.subcommand(boot_node::cli_app())
|
||||
.subcommand(validator_client::cli_app())
|
||||
@ -167,8 +178,15 @@ fn run<E: EthSpec>(
|
||||
|
||||
let log_format = matches.value_of("log-format");
|
||||
|
||||
let optional_testnet_config =
|
||||
clap_utils::parse_testnet_dir_with_hardcoded_default(matches, "testnet-dir")?;
|
||||
// Parse testnet config from the `testnet` and `testnet-dir` flag in that order
|
||||
// else, use the default
|
||||
let mut optional_testnet_config = Eth2TestnetConfig::hard_coded_default()?;
|
||||
if matches.is_present("testnet") {
|
||||
optional_testnet_config = clap_utils::parse_hardcoded_network(matches, "testnet")?;
|
||||
};
|
||||
if matches.is_present("testnet-dir") {
|
||||
optional_testnet_config = clap_utils::parse_testnet_dir(matches, "testnet-dir")?;
|
||||
};
|
||||
|
||||
let mut environment = environment_builder
|
||||
.async_logger(debug_level, log_format)?
|
||||
@ -193,7 +211,19 @@ fn run<E: EthSpec>(
|
||||
//
|
||||
// Creating a command which can run both might be useful future works.
|
||||
|
||||
// Print an indication of which network is currently in use.
|
||||
let optional_testnet = clap_utils::parse_optional::<String>(matches, "testnet")?;
|
||||
let optional_testnet_dir = clap_utils::parse_optional::<PathBuf>(matches, "testnet-dir")?;
|
||||
|
||||
let testnet_name = match (optional_testnet, optional_testnet_dir) {
|
||||
(Some(testnet), None) => testnet,
|
||||
(None, Some(testnet_dir)) => format!("custom ({})", testnet_dir.display()),
|
||||
(None, None) => DEFAULT_HARDCODED_TESTNET.to_string(),
|
||||
(Some(_), Some(_)) => panic!("CLI prevents both --testnet and --testnet-dir"),
|
||||
};
|
||||
|
||||
if let Some(sub_matches) = matches.subcommand_matches("account_manager") {
|
||||
eprintln!("Running account manager for {} testnet", testnet_name);
|
||||
// Pass the entire `environment` to the account manager so it can run blocking operations.
|
||||
account_manager::run(sub_matches, environment)?;
|
||||
|
||||
@ -205,14 +235,11 @@ fn run<E: EthSpec>(
|
||||
log,
|
||||
"Ethereum 2.0 is pre-release. This software is experimental."
|
||||
);
|
||||
|
||||
if !matches.is_present("testnet-dir") {
|
||||
info!(
|
||||
log,
|
||||
"Using default testnet";
|
||||
"default" => HARDCODED_TESTNET
|
||||
)
|
||||
}
|
||||
info!(
|
||||
log,
|
||||
"Configured for testnet";
|
||||
"name" => testnet_name
|
||||
);
|
||||
|
||||
let beacon_node = if let Some(sub_matches) = matches.subcommand_matches("beacon_node") {
|
||||
let runtime_context = environment.core_context();
|
||||
|
Loading…
Reference in New Issue
Block a user