Skip to content

Commit

Permalink
Fix local testnet scripts (#2229)
Browse files Browse the repository at this point in the history
## Issue Addressed

Resolves #2094 

## Proposed Changes

Fixes scripts for creating local testnets. Adds an option in `lighthouse boot_node` to run with a previously generated enr.
  • Loading branch information
pawanjay176 committed Mar 30, 2021
1 parent 9eb1945 commit 95a3622
Show file tree
Hide file tree
Showing 18 changed files with 359 additions and 140 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions beacon_node/eth2_libp2p/src/discovery/enr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,21 @@ fn compare_enr(local_enr: &Enr, disk_enr: &Enr) -> bool {
&& local_enr.get(BITFIELD_ENR_KEY) == disk_enr.get(BITFIELD_ENR_KEY)
}

/// Loads enr from the given directory
pub fn load_enr_from_disk(dir: &Path) -> Result<Enr, String> {
let enr_f = dir.join(ENR_FILENAME);
let mut enr_file =
File::open(enr_f).map_err(|e| format!("Failed to open enr file: {:?}", e))?;
let mut enr_string = String::new();
match enr_file.read_to_string(&mut enr_string) {
Err(_) => Err("Could not read ENR from file".to_string()),
Ok(_) => match Enr::from_str(&enr_string) {
Ok(disk_enr) => Ok(disk_enr),
Err(e) => Err(format!("ENR from file could not be decoded: {:?}", e)),
},
}
}

/// Saves an ENR to disk
pub fn save_enr_to_disk(dir: &Path, enr: &Enr, log: &slog::Logger) {
let _ = std::fs::create_dir_all(dir);
Expand Down
5 changes: 4 additions & 1 deletion beacon_node/eth2_libp2p/src/discovery/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ pub(crate) mod enr;
pub mod enr_ext;

// Allow external use of the lighthouse ENR builder
pub use enr::{build_enr, create_enr_builder_from_config, use_or_load_enr, CombinedKey, Eth2Enr};
pub use enr::{
build_enr, create_enr_builder_from_config, load_enr_from_disk, use_or_load_enr, CombinedKey,
Eth2Enr,
};
pub use enr_ext::{peer_id_to_node_id, CombinedKeyExt, EnrExt};
pub use libp2p::core::identity::{Keypair, PublicKey};

Expand Down
16 changes: 13 additions & 3 deletions boot_node/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
If a DNS address is provided, the enr-address is set to the IP address it resolves to and \
does not auto-update based on PONG responses in discovery.")
.required(true)
.takes_value(true),
.takes_value(true)
.conflicts_with("network-dir")
)
.arg(
Arg::with_name("port")
.long("port")
.value_name("PORT")
.help("The UDP port to listen on.")
.default_value("9000")
.takes_value(true),
.takes_value(true)
)
.arg(
Arg::with_name("listen-address")
Expand All @@ -48,7 +50,8 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.long("enr-port")
.value_name("PORT")
.help("The UDP port of the boot node's ENR. This is the port that external peers will dial to reach this boot node. Set this only if the external port differs from the listening port.")
.takes_value(true),
.takes_value(true)
.conflicts_with("network-dir")
)
.arg(
Arg::with_name("enable-enr-auto-update")
Expand All @@ -57,4 +60,11 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
.help("Discovery can automatically update the node's local ENR with an external IP address and port as seen by other peers on the network. \
This enables this feature.")
)
.arg(
Arg::with_name("network-dir")
.value_name("NETWORK_DIR")
.long("network-dir")
.help("The directory which contains the enr and it's assoicated private key")
.takes_value(true)
)
}
104 changes: 55 additions & 49 deletions boot_node/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ use beacon_node::{get_data_dir, get_eth2_network_config, set_network_config};
use clap::ArgMatches;
use eth2_libp2p::discv5::{enr::CombinedKey, Enr};
use eth2_libp2p::{
discovery::{create_enr_builder_from_config, use_or_load_enr},
discovery::{create_enr_builder_from_config, load_enr_from_disk, use_or_load_enr},
load_private_key, CombinedKeyExt, NetworkConfig,
};
use ssz::Encode;
use std::convert::TryFrom;
use std::marker::PhantomData;
use std::net::SocketAddr;
use std::{marker::PhantomData, path::PathBuf};
use types::EthSpec;

/// A set of configuration parameters for the bootnode, established from CLI arguments.
Expand Down Expand Up @@ -68,59 +68,65 @@ impl<T: EthSpec> TryFrom<&ArgMatches<'_>> for BootNodeConfig<T> {
);
}

let private_key = load_private_key(&network_config, &logger);
let local_key = CombinedKey::from_libp2p(&private_key)?;

// build the enr_fork_id and add it to the local_enr if it exists
let enr_fork = {
let spec = eth2_network_config
.yaml_config
.as_ref()
.ok_or("The network directory must contain a spec config")?
.apply_to_chain_spec::<T>(&T::default_spec())
.ok_or("The loaded config is not compatible with the current spec")?;

if eth2_network_config.beacon_state_is_known() {
let genesis_state = eth2_network_config.beacon_state::<T>()?;

slog::info!(logger, "Genesis state found"; "root" => genesis_state.canonical_root().to_string());
let enr_fork = spec.enr_fork_id(
types::Slot::from(0u64),
genesis_state.genesis_validators_root,
);

Some(enr_fork.as_ssz_bytes())
} else {
slog::warn!(
logger,
"No genesis state provided. No Eth2 field added to the ENR"
);
None
}
};

// Build the local ENR

let mut local_enr = {
let mut builder = create_enr_builder_from_config(&network_config, false);

// If we know of the ENR field, add it to the initial construction
if let Some(enr_fork_bytes) = enr_fork {
builder.add_value("eth2", &enr_fork_bytes);
}
builder
.build(&local_key)
.map_err(|e| format!("Failed to build ENR: {:?}", e))?
};

use_or_load_enr(&local_key, &mut local_enr, &network_config, &logger)?;

let auto_update = matches.is_present("enable-enr_auto_update");

// the address to listen on
let listen_socket =
SocketAddr::new(network_config.listen_address, network_config.discovery_port);

let private_key = load_private_key(&network_config, &logger);
let local_key = CombinedKey::from_libp2p(&private_key)?;

let local_enr = if let Some(dir) = matches.value_of("network-dir") {
let network_dir: PathBuf = dir.into();
load_enr_from_disk(&network_dir)?
} else {
// build the enr_fork_id and add it to the local_enr if it exists
let enr_fork = {
let spec = eth2_network_config
.yaml_config
.as_ref()
.ok_or("The network directory must contain a spec config")?
.apply_to_chain_spec::<T>(&T::default_spec())
.ok_or("The loaded config is not compatible with the current spec")?;

if eth2_network_config.beacon_state_is_known() {
let genesis_state = eth2_network_config.beacon_state::<T>()?;

slog::info!(logger, "Genesis state found"; "root" => genesis_state.canonical_root().to_string());
let enr_fork = spec.enr_fork_id(
types::Slot::from(0u64),
genesis_state.genesis_validators_root,
);

Some(enr_fork.as_ssz_bytes())
} else {
slog::warn!(
logger,
"No genesis state provided. No Eth2 field added to the ENR"
);
None
}
};

// Build the local ENR

let mut local_enr = {
let mut builder = create_enr_builder_from_config(&network_config, false);

// If we know of the ENR field, add it to the initial construction
if let Some(enr_fork_bytes) = enr_fork {
builder.add_value("eth2", &enr_fork_bytes);
}
builder
.build(&local_key)
.map_err(|e| format!("Failed to build ENR: {:?}", e))?
};

use_or_load_enr(&local_key, &mut local_enr, &network_config, &logger)?;
local_enr
};

Ok(BootNodeConfig {
listen_socket,
boot_nodes,
Expand Down
2 changes: 2 additions & 0 deletions lcli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ lighthouse_version = { path = "../common/lighthouse_version" }
directory = { path = "../common/directory" }
account_utils = { path = "../common/account_utils" }
eth2_wallet = { path = "../crypto/eth2_wallet" }
web3 = "0.14.0"
eth1_test_rig = { path = "../testing/eth1_test_rig" }
33 changes: 33 additions & 0 deletions lcli/src/deploy_deposit_contract.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use clap::ArgMatches;
use environment::Environment;
use types::EthSpec;

use web3::{transports::Http, Web3};

pub fn run<T: EthSpec>(env: Environment<T>, matches: &ArgMatches<'_>) -> Result<(), String> {
let eth1_http: String = clap_utils::parse_required(matches, "eth1-http")?;
let confirmations: usize = clap_utils::parse_required(matches, "confirmations")?;
let validator_count: Option<usize> = clap_utils::parse_optional(matches, "validator-count")?;

let transport =
Http::new(&eth1_http).map_err(|e| format!("Unable to connect to eth1 HTTP: {:?}", e))?;
let web3 = Web3::new(transport);

env.runtime().block_on(async {
let contract = eth1_test_rig::DepositContract::deploy(web3, confirmations, None)
.await
.map_err(|e| format!("Failed to deploy deposit contract: {:?}", e))?;

println!("Deposit contract address: {:?}", contract.address());

// Deposit insecure validators to the deposit contract created
if let Some(validator_count) = validator_count {
let amount = env.eth2_config.spec.max_effective_balance;
for i in 0..validator_count {
println!("Submitting deposit for validator {}...", i);
contract.deposit_deterministic_async::<T>(i, amount).await?;
}
}
Ok(())
})
}
46 changes: 38 additions & 8 deletions lcli/src/insecure_validators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ use std::fs;
use std::path::PathBuf;
use validator_dir::Builder as ValidatorBuilder;

pub fn run(matches: &ArgMatches) -> Result<(), String> {
let validator_count: usize = clap_utils::parse_required(matches, "count")?;
let validators_dir: PathBuf = clap_utils::parse_required(matches, "validators-dir")?;
let secrets_dir: PathBuf = clap_utils::parse_required(matches, "secrets-dir")?;

/// Generates validator directories with INSECURE, deterministic keypairs given the range
/// of indices, validator and secret directories.
pub fn generate_validator_dirs(
indices: &[usize],
validators_dir: PathBuf,
secrets_dir: PathBuf,
) -> Result<(), String> {
if !validators_dir.exists() {
fs::create_dir_all(&validators_dir)
.map_err(|e| format!("Unable to create validators dir: {:?}", e))?;
Expand All @@ -18,17 +20,45 @@ pub fn run(matches: &ArgMatches) -> Result<(), String> {
.map_err(|e| format!("Unable to create secrets dir: {:?}", e))?;
}

for i in 0..validator_count {
println!("Validator {}/{}", i + 1, validator_count);
for i in indices {
println!("Validator {}", i + 1);

ValidatorBuilder::new(validators_dir.clone())
.password_dir(secrets_dir.clone())
.store_withdrawal_keystore(false)
.insecure_voting_keypair(i)
.insecure_voting_keypair(*i)
.map_err(|e| format!("Unable to generate keys: {:?}", e))?
.build()
.map_err(|e| format!("Unable to build validator: {:?}", e))?;
}

Ok(())
}

pub fn run(matches: &ArgMatches) -> Result<(), String> {
let validator_count: usize = clap_utils::parse_required(matches, "count")?;
let base_dir: PathBuf = clap_utils::parse_required(matches, "base-dir")?;
let node_count: Option<usize> = clap_utils::parse_optional(matches, "node-count")?;
if let Some(node_count) = node_count {
let validators_per_node = validator_count / node_count;
let validator_range = (0..validator_count).collect::<Vec<_>>();
let indices_range = validator_range
.chunks(validators_per_node)
.collect::<Vec<_>>();

for (i, indices) in indices_range.iter().enumerate() {
let validators_dir = base_dir.join(format!("node_{}", i + 1)).join("validators");
let secrets_dir = base_dir.join(format!("node_{}", i + 1)).join("secrets");
generate_validator_dirs(indices, validators_dir, secrets_dir)?;
}
} else {
let validators_dir = base_dir.join("validators");
let secrets_dir = base_dir.join("secrets");
generate_validator_dirs(
(0..validator_count).collect::<Vec<_>>().as_slice(),
validators_dir,
secrets_dir,
)?;
}
Ok(())
}
Loading

0 comments on commit 95a3622

Please sign in to comment.