cdec3cec18
- Resolves #1550 - Resolves #824 - Resolves #825 - Resolves #1131 - Resolves #1411 - Resolves #1256 - Resolve #1177 - Includes the `ShufflingId` struct initially defined in #1492. That PR is now closed and the changes are included here, with significant bug fixes. - Implement the https://github.com/ethereum/eth2.0-APIs in a new `http_api` crate using `warp`. This replaces the `rest_api` crate. - Add a new `common/eth2` crate which provides a wrapper around `reqwest`, providing the HTTP client that is used by the validator client and for testing. This replaces the `common/remote_beacon_node` crate. - Create a `http_metrics` crate which is a dedicated server for Prometheus metrics (they are no longer served on the same port as the REST API). We now have flags for `--metrics`, `--metrics-address`, etc. - Allow the `subnet_id` to be an optional parameter for `VerifiedUnaggregatedAttestation::verify`. This means it does not need to be provided unnecessarily by the validator client. - Move `fn map_attestation_committee` in `mod beacon_chain::attestation_verification` to a new `fn with_committee_cache` on the `BeaconChain` so the same cache can be used for obtaining validator duties. - Add some other helpers to `BeaconChain` to assist with common API duties (e.g., `block_root_at_slot`, `head_beacon_block_root`). - Change the `NaiveAggregationPool` so it can index attestations by `hash_tree_root(attestation.data)`. This is a requirement of the API. - Add functions to `BeaconChainHarness` to allow it to create slashings and exits. - Allow for `eth1::Eth1NetworkId` to go to/from a `String`. - Add functions to the `OperationPool` to allow getting all objects in the pool. - Add function to `BeaconState` to check if a committee cache is initialized. - Fix bug where `seconds_per_eth1_block` was not transferring over from `YamlConfig` to `ChainSpec`. - Add the `deposit_contract_address` to `YamlConfig` and `ChainSpec`. We needed to be able to return it in an API response. - Change some uses of serde `serialize_with` and `deserialize_with` to a single use of `with` (code quality). - Impl `Display` and `FromStr` for several BLS fields. - Check for clock discrepancy when VC polls BN for sync state (with +/- 1 slot tolerance). This is not intended to be comprehensive, it was just easy to do. - See #1434 for a per-endpoint overview. - Seeking clarity here: https://github.com/ethereum/eth2.0-APIs/issues/75 - [x] Add docs for prom port to close #1256 - [x] Follow up on this #1177 - [x] ~~Follow up with #1424~~ Will fix in future PR. - [x] Follow up with #1411 - [x] ~~Follow up with #1260~~ Will fix in future PR. - [x] Add quotes to all integers. - [x] Remove `rest_types` - [x] Address missing beacon block error. (#1629) - [x] ~~Add tests for lighthouse/peers endpoints~~ Wontfix - [x] ~~Follow up with validator status proposal~~ Tracked in #1434 - [x] Unify graffiti structs - [x] ~~Start server when waiting for genesis?~~ Will fix in future PR. - [x] TODO in http_api tests - [x] Move lighthouse endpoints off /eth/v1 - [x] Update docs to link to standard - ~~Blocked on #1586~~ Co-authored-by: Michael Sproul <michael@sigmaprime.io>
361 lines
11 KiB
Rust
361 lines
11 KiB
Rust
use crate::checks::{epoch_delay, verify_all_finalized_at};
|
|
use crate::local_network::LocalNetwork;
|
|
use clap::ArgMatches;
|
|
use futures::prelude::*;
|
|
use node_test_rig::ClientConfig;
|
|
use node_test_rig::{
|
|
environment::EnvironmentBuilder, testing_client_config, ClientGenesis, ValidatorConfig,
|
|
ValidatorFiles,
|
|
};
|
|
use std::net::{IpAddr, Ipv4Addr};
|
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
|
use types::{Epoch, EthSpec};
|
|
|
|
pub fn run_syncing_sim(matches: &ArgMatches) -> Result<(), String> {
|
|
let initial_delay = value_t!(matches, "initial_delay", u64).unwrap();
|
|
let sync_timeout = value_t!(matches, "sync_timeout", u64).unwrap();
|
|
let speed_up_factor = value_t!(matches, "speedup", u64).unwrap();
|
|
let strategy = value_t!(matches, "strategy", String).unwrap();
|
|
|
|
println!("Syncing Simulator:");
|
|
println!(" initial_delay:{}", initial_delay);
|
|
println!(" sync timeout: {}", sync_timeout);
|
|
println!(" speed up factor:{}", speed_up_factor);
|
|
println!(" strategy:{}", strategy);
|
|
|
|
let log_level = "debug";
|
|
let log_format = None;
|
|
|
|
syncing_sim(
|
|
speed_up_factor,
|
|
initial_delay,
|
|
sync_timeout,
|
|
strategy,
|
|
log_level,
|
|
log_format,
|
|
)
|
|
}
|
|
|
|
fn syncing_sim(
|
|
speed_up_factor: u64,
|
|
initial_delay: u64,
|
|
sync_timeout: u64,
|
|
strategy: String,
|
|
log_level: &str,
|
|
log_format: Option<&str>,
|
|
) -> Result<(), String> {
|
|
let mut env = EnvironmentBuilder::minimal()
|
|
.async_logger(log_level, log_format)?
|
|
.multi_threaded_tokio_runtime()?
|
|
.build()?;
|
|
|
|
let spec = &mut env.eth2_config.spec;
|
|
let end_after_checks = true;
|
|
let eth1_block_time = Duration::from_millis(15_000 / speed_up_factor);
|
|
|
|
spec.milliseconds_per_slot /= speed_up_factor;
|
|
spec.eth1_follow_distance = 16;
|
|
spec.genesis_delay = eth1_block_time.as_secs() * spec.eth1_follow_distance * 2;
|
|
spec.min_genesis_time = 0;
|
|
spec.min_genesis_active_validator_count = 64;
|
|
spec.seconds_per_eth1_block = 1;
|
|
|
|
let num_validators = 8;
|
|
let slot_duration = Duration::from_millis(spec.milliseconds_per_slot);
|
|
let context = env.core_context();
|
|
let mut beacon_config = testing_client_config();
|
|
|
|
let genesis_time = SystemTime::now()
|
|
.duration_since(UNIX_EPOCH)
|
|
.map_err(|_| "should get system time")?
|
|
+ Duration::from_secs(5);
|
|
beacon_config.genesis = ClientGenesis::Interop {
|
|
validator_count: num_validators,
|
|
genesis_time: genesis_time.as_secs(),
|
|
};
|
|
beacon_config.dummy_eth1_backend = true;
|
|
beacon_config.sync_eth1_chain = true;
|
|
|
|
beacon_config.network.enr_address = Some(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)));
|
|
|
|
// Generate the directories and keystores required for the validator clients.
|
|
let validator_indices = (0..num_validators).collect::<Vec<_>>();
|
|
let validator_files = ValidatorFiles::with_keystores(&validator_indices).unwrap();
|
|
|
|
let main_future = async {
|
|
/*
|
|
* Create a new `LocalNetwork` with one beacon node.
|
|
*/
|
|
let network = LocalNetwork::new(context, beacon_config.clone()).await?;
|
|
|
|
/*
|
|
* Add a validator client which handles all validators from the genesis state.
|
|
*/
|
|
network
|
|
.add_validator_client(ValidatorConfig::default(), 0, validator_files)
|
|
.await?;
|
|
|
|
// Check all syncing strategies one after other.
|
|
pick_strategy(
|
|
&strategy,
|
|
network.clone(),
|
|
beacon_config.clone(),
|
|
slot_duration,
|
|
initial_delay,
|
|
sync_timeout,
|
|
)
|
|
.await?;
|
|
|
|
// The `final_future` either completes immediately or never completes, depending on the value
|
|
// of `end_after_checks`.
|
|
|
|
if !end_after_checks {
|
|
future::pending::<()>().await;
|
|
}
|
|
|
|
/*
|
|
* End the simulation by dropping the network. This will kill all running beacon nodes and
|
|
* validator clients.
|
|
*/
|
|
println!(
|
|
"Simulation complete. Finished with {} beacon nodes and {} validator clients",
|
|
network.beacon_node_count(),
|
|
network.validator_client_count()
|
|
);
|
|
|
|
// Be explicit about dropping the network, as this kills all the nodes. This ensures
|
|
// all the checks have adequate time to pass.
|
|
drop(network);
|
|
Ok::<(), String>(())
|
|
};
|
|
|
|
env.runtime().block_on(main_future)
|
|
}
|
|
|
|
pub async fn pick_strategy<E: EthSpec>(
|
|
strategy: &str,
|
|
network: LocalNetwork<E>,
|
|
beacon_config: ClientConfig,
|
|
slot_duration: Duration,
|
|
initial_delay: u64,
|
|
sync_timeout: u64,
|
|
) -> Result<(), String> {
|
|
match strategy {
|
|
"one-node" => {
|
|
verify_one_node_sync(
|
|
network,
|
|
beacon_config,
|
|
slot_duration,
|
|
initial_delay,
|
|
sync_timeout,
|
|
)
|
|
.await
|
|
}
|
|
"two-nodes" => {
|
|
verify_two_nodes_sync(
|
|
network,
|
|
beacon_config,
|
|
slot_duration,
|
|
initial_delay,
|
|
sync_timeout,
|
|
)
|
|
.await
|
|
}
|
|
"mixed" => {
|
|
verify_in_between_sync(
|
|
network,
|
|
beacon_config,
|
|
slot_duration,
|
|
initial_delay,
|
|
sync_timeout,
|
|
)
|
|
.await
|
|
}
|
|
"all" => {
|
|
verify_syncing(
|
|
network,
|
|
beacon_config,
|
|
slot_duration,
|
|
initial_delay,
|
|
sync_timeout,
|
|
)
|
|
.await
|
|
}
|
|
_ => Err("Invalid strategy".into()),
|
|
}
|
|
}
|
|
|
|
/// Verify one node added after `initial_delay` epochs is in sync
|
|
/// after `sync_timeout` epochs.
|
|
pub async fn verify_one_node_sync<E: EthSpec>(
|
|
network: LocalNetwork<E>,
|
|
beacon_config: ClientConfig,
|
|
slot_duration: Duration,
|
|
initial_delay: u64,
|
|
sync_timeout: u64,
|
|
) -> Result<(), String> {
|
|
let epoch_duration = slot_duration * (E::slots_per_epoch() as u32);
|
|
let network_c = network.clone();
|
|
// Delay for `initial_delay` epochs before adding another node to start syncing
|
|
epoch_delay(
|
|
Epoch::new(initial_delay),
|
|
slot_duration,
|
|
E::slots_per_epoch(),
|
|
)
|
|
.await;
|
|
// Add a beacon node
|
|
network.add_beacon_node(beacon_config).await?;
|
|
// Check every `epoch_duration` if nodes are synced
|
|
// limited to at most `sync_timeout` epochs
|
|
let mut interval = tokio::time::interval(epoch_duration);
|
|
let mut count = 0;
|
|
while interval.next().await.is_some() {
|
|
if count >= sync_timeout || !check_still_syncing(&network_c).await? {
|
|
break;
|
|
}
|
|
count += 1;
|
|
}
|
|
let epoch = network.bootnode_epoch().await?;
|
|
verify_all_finalized_at(network, epoch)
|
|
.map_err(|e| format!("One node sync error: {}", e))
|
|
.await
|
|
}
|
|
|
|
/// Verify two nodes added after `initial_delay` epochs are in sync
|
|
/// after `sync_timeout` epochs.
|
|
pub async fn verify_two_nodes_sync<E: EthSpec>(
|
|
network: LocalNetwork<E>,
|
|
beacon_config: ClientConfig,
|
|
slot_duration: Duration,
|
|
initial_delay: u64,
|
|
sync_timeout: u64,
|
|
) -> Result<(), String> {
|
|
let epoch_duration = slot_duration * (E::slots_per_epoch() as u32);
|
|
let network_c = network.clone();
|
|
// Delay for `initial_delay` epochs before adding another node to start syncing
|
|
epoch_delay(
|
|
Epoch::new(initial_delay),
|
|
slot_duration,
|
|
E::slots_per_epoch(),
|
|
)
|
|
.await;
|
|
// Add beacon nodes
|
|
network.add_beacon_node(beacon_config.clone()).await?;
|
|
network.add_beacon_node(beacon_config).await?;
|
|
// Check every `epoch_duration` if nodes are synced
|
|
// limited to at most `sync_timeout` epochs
|
|
let mut interval = tokio::time::interval(epoch_duration);
|
|
let mut count = 0;
|
|
while interval.next().await.is_some() {
|
|
if count >= sync_timeout || !check_still_syncing(&network_c).await? {
|
|
break;
|
|
}
|
|
count += 1;
|
|
}
|
|
let epoch = network.bootnode_epoch().await?;
|
|
verify_all_finalized_at(network, epoch)
|
|
.map_err(|e| format!("One node sync error: {}", e))
|
|
.await
|
|
}
|
|
|
|
/// Add 2 syncing nodes after `initial_delay` epochs,
|
|
/// Add another node after `sync_timeout - 5` epochs and verify all are
|
|
/// in sync after `sync_timeout + 5` epochs.
|
|
pub async fn verify_in_between_sync<E: EthSpec>(
|
|
network: LocalNetwork<E>,
|
|
beacon_config: ClientConfig,
|
|
slot_duration: Duration,
|
|
initial_delay: u64,
|
|
sync_timeout: u64,
|
|
) -> Result<(), String> {
|
|
let epoch_duration = slot_duration * (E::slots_per_epoch() as u32);
|
|
let network_c = network.clone();
|
|
// Delay for `initial_delay` epochs before adding another node to start syncing
|
|
let config1 = beacon_config.clone();
|
|
epoch_delay(
|
|
Epoch::new(initial_delay),
|
|
slot_duration,
|
|
E::slots_per_epoch(),
|
|
)
|
|
.await;
|
|
// Add two beacon nodes
|
|
network.add_beacon_node(beacon_config.clone()).await?;
|
|
network.add_beacon_node(beacon_config).await?;
|
|
// Delay before adding additional syncing nodes.
|
|
epoch_delay(
|
|
Epoch::new(sync_timeout - 5),
|
|
slot_duration,
|
|
E::slots_per_epoch(),
|
|
)
|
|
.await;
|
|
// Add a beacon node
|
|
network.add_beacon_node(config1.clone()).await?;
|
|
// Check every `epoch_duration` if nodes are synced
|
|
// limited to at most `sync_timeout` epochs
|
|
let mut interval = tokio::time::interval(epoch_duration);
|
|
let mut count = 0;
|
|
while interval.next().await.is_some() {
|
|
if count >= sync_timeout || !check_still_syncing(&network_c).await? {
|
|
break;
|
|
}
|
|
count += 1;
|
|
}
|
|
let epoch = network.bootnode_epoch().await?;
|
|
verify_all_finalized_at(network, epoch)
|
|
.map_err(|e| format!("One node sync error: {}", e))
|
|
.await
|
|
}
|
|
|
|
/// Run syncing strategies one after other.
|
|
pub async fn verify_syncing<E: EthSpec>(
|
|
network: LocalNetwork<E>,
|
|
beacon_config: ClientConfig,
|
|
slot_duration: Duration,
|
|
initial_delay: u64,
|
|
sync_timeout: u64,
|
|
) -> Result<(), String> {
|
|
verify_one_node_sync(
|
|
network.clone(),
|
|
beacon_config.clone(),
|
|
slot_duration,
|
|
initial_delay,
|
|
sync_timeout,
|
|
)
|
|
.await?;
|
|
println!("Completed one node sync");
|
|
verify_two_nodes_sync(
|
|
network.clone(),
|
|
beacon_config.clone(),
|
|
slot_duration,
|
|
initial_delay,
|
|
sync_timeout,
|
|
)
|
|
.await?;
|
|
println!("Completed two node sync");
|
|
verify_in_between_sync(
|
|
network,
|
|
beacon_config,
|
|
slot_duration,
|
|
initial_delay,
|
|
sync_timeout,
|
|
)
|
|
.await?;
|
|
println!("Completed in between sync");
|
|
Ok(())
|
|
}
|
|
|
|
pub async fn check_still_syncing<E: EthSpec>(network: &LocalNetwork<E>) -> Result<bool, String> {
|
|
// get syncing status of nodes
|
|
let mut status = Vec::new();
|
|
for remote_node in network.remote_nodes()? {
|
|
status.push(
|
|
remote_node
|
|
.get_node_syncing()
|
|
.await
|
|
.map(|body| body.data.is_syncing)
|
|
.map_err(|e| format!("Get syncing status via http failed: {:?}", e))?,
|
|
)
|
|
}
|
|
Ok(status.iter().any(|is_syncing| *is_syncing))
|
|
}
|