mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-04 09:11:42 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9eb1945136 | ||
|
|
3d239b85ac | ||
|
|
03cefd0065 | ||
|
|
39928d5c69 | ||
|
|
d3d8c22edf | ||
|
|
f9d60f5436 | ||
|
|
9a71a7e486 | ||
|
|
d18bba588b |
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -628,7 +628,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "beacon_node"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
dependencies = [
|
||||
"beacon_chain",
|
||||
"clap",
|
||||
@@ -841,7 +841,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "boot_node"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
dependencies = [
|
||||
"beacon_node",
|
||||
"clap",
|
||||
@@ -3335,7 +3335,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lcli"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
dependencies = [
|
||||
"account_utils",
|
||||
"bls",
|
||||
@@ -3707,7 +3707,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lighthouse"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
dependencies = [
|
||||
"account_manager",
|
||||
"account_utils",
|
||||
|
||||
6
Makefile
6
Makefile
@@ -121,7 +121,11 @@ test-full: cargo-fmt test-release test-debug test-ef
|
||||
# Lints the code for bad style and potentially unsafe arithmetic using Clippy.
|
||||
# Clippy lints are opt-in per-crate for now. By default, everything is allowed except for performance and correctness lints.
|
||||
lint:
|
||||
cargo clippy --all --tests -- -D warnings
|
||||
cargo clippy --all --tests -- \
|
||||
-D warnings \
|
||||
-A clippy::from-over-into \
|
||||
-A clippy::upper-case-acronyms \
|
||||
-A clippy::vec-init-then-push
|
||||
|
||||
# Runs the makefile in the `ef_tests` repo.
|
||||
#
|
||||
|
||||
@@ -69,8 +69,8 @@ The best place for discussion is the [Lighthouse Discord
|
||||
server](https://discord.gg/cyAszAh). Alternatively, you may use the
|
||||
[sigp/lighthouse gitter](https://gitter.im/sigp/lighthouse).
|
||||
|
||||
Sign up to the [Lighthouse Development Updates](https://mailchi.mp/3d9df0417779/lighthouse-dev-updates)
|
||||
mailing list for email notifications about releases, network status and other important information.
|
||||
Sign up to the [Lighthouse Development Updates](http://eepurl.com/dh9Lvb) mailing list for email
|
||||
notifications about releases, network status and other important information.
|
||||
|
||||
Encrypt sensitive messages using our [PGP
|
||||
key](https://keybase.io/sigp/pgp_keys.asc?fingerprint=15e66d941f697e28f49381f426416dc3f30674b0).
|
||||
|
||||
@@ -10,7 +10,7 @@ use eth2_keystore::Keystore;
|
||||
use eth2_network_config::Eth2NetworkConfig;
|
||||
use safe_arith::SafeArith;
|
||||
use slot_clock::{SlotClock, SystemTimeSlotClock};
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
use types::{ChainSpec, Epoch, EthSpec, Fork, VoluntaryExit};
|
||||
@@ -91,7 +91,7 @@ pub fn cli_run<E: EthSpec>(matches: &ArgMatches, env: Environment<E>) -> Result<
|
||||
|
||||
/// Gets the keypair and validator_index for every validator and calls `publish_voluntary_exit` on it.
|
||||
async fn publish_voluntary_exit<E: EthSpec>(
|
||||
keystore_path: &PathBuf,
|
||||
keystore_path: &Path,
|
||||
password_file_path: Option<&PathBuf>,
|
||||
client: &BeaconNodeHttpClient,
|
||||
spec: &ChainSpec,
|
||||
@@ -310,7 +310,7 @@ fn get_current_epoch<E: EthSpec>(genesis_time: u64, spec: &ChainSpec) -> Option<
|
||||
/// If the `password_file_path` is Some, unlock keystore using password in given file
|
||||
/// otherwise, prompts user for a password to unlock the keystore.
|
||||
fn load_voting_keypair(
|
||||
voting_keystore_path: &PathBuf,
|
||||
voting_keystore_path: &Path,
|
||||
password_file_path: Option<&PathBuf>,
|
||||
stdin_inputs: bool,
|
||||
) -> Result<Keypair, String> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "beacon_node"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>", "Age Manning <Age@AgeManning.com"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -252,11 +252,18 @@ impl<T: Item, E: EthSpec> AutoPruningContainer<T, E> {
|
||||
|
||||
/// The maximum number of epochs stored in `self`.
|
||||
fn max_capacity(&self) -> u64 {
|
||||
// The current epoch and the previous epoch. This is sufficient whilst
|
||||
// GOSSIP_CLOCK_DISPARITY is 1/2 a slot or less:
|
||||
// The next, current and previous epochs. We require the next epoch due to the
|
||||
// `MAXIMUM_GOSSIP_CLOCK_DISPARITY`. We require the previous epoch since the
|
||||
// specification delcares:
|
||||
//
|
||||
// https://github.com/ethereum/eth2.0-specs/pull/1706#issuecomment-610151808
|
||||
2
|
||||
// ```
|
||||
// aggregate.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE
|
||||
// >= current_slot >= aggregate.data.slot
|
||||
// ```
|
||||
//
|
||||
// This means that during the current epoch we will always accept an attestation
|
||||
// from at least one slot in the previous epoch.
|
||||
3
|
||||
}
|
||||
|
||||
/// Updates `self` with the current epoch, removing all attestations that become expired
|
||||
|
||||
@@ -53,7 +53,7 @@ impl Inner {
|
||||
pub fn from_bytes(bytes: &[u8], config: Config, spec: ChainSpec) -> Result<Self, String> {
|
||||
let ssz_cache = SszEth1Cache::from_ssz_bytes(bytes)
|
||||
.map_err(|e| format!("Ssz decoding error: {:?}", e))?;
|
||||
Ok(ssz_cache.to_inner(config, spec)?)
|
||||
ssz_cache.to_inner(config, spec)
|
||||
}
|
||||
|
||||
/// Returns a reference to the specification.
|
||||
|
||||
@@ -36,7 +36,7 @@ use ssz::Encode;
|
||||
use std::collections::HashSet;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::{
|
||||
collections::VecDeque,
|
||||
marker::PhantomData,
|
||||
@@ -1336,7 +1336,7 @@ impl<TSpec: EthSpec> std::convert::From<Response<TSpec>> for RPCCodedResponse<TS
|
||||
}
|
||||
|
||||
/// Persist metadata to disk
|
||||
pub fn save_metadata_to_disk<E: EthSpec>(dir: &PathBuf, metadata: MetaData<E>, log: &slog::Logger) {
|
||||
pub fn save_metadata_to_disk<E: EthSpec>(dir: &Path, metadata: MetaData<E>, log: &slog::Logger) {
|
||||
let _ = std::fs::create_dir_all(&dir);
|
||||
match File::create(dir.join(METADATA_FILENAME))
|
||||
.and_then(|mut f| f.write_all(&metadata.as_ssz_bytes()))
|
||||
|
||||
@@ -474,7 +474,7 @@ fn strip_peer_id(addr: &mut Multiaddr) {
|
||||
|
||||
/// Load metadata from persisted file. Return default metadata if loading fails.
|
||||
fn load_or_build_metadata<E: EthSpec>(
|
||||
network_dir: &std::path::PathBuf,
|
||||
network_dir: &std::path::Path,
|
||||
log: &slog::Logger,
|
||||
) -> MetaData<E> {
|
||||
// Default metadata
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
//! Contains the handler for the `GET validator/duties/attester/{epoch}` endpoint.
|
||||
|
||||
use crate::state_id::StateId;
|
||||
use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes};
|
||||
use beacon_chain::{
|
||||
BeaconChain, BeaconChainError, BeaconChainTypes, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
|
||||
};
|
||||
use eth2::types::{self as api_types};
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::state_advance::partial_state_advance;
|
||||
use types::{
|
||||
AttestationDuty, BeaconState, ChainSpec, CloneConfig, Epoch, EthSpec, Hash256, RelativeEpoch,
|
||||
@@ -20,16 +23,32 @@ pub fn attester_duties<T: BeaconChainTypes>(
|
||||
let current_epoch = chain
|
||||
.epoch()
|
||||
.map_err(warp_utils::reject::beacon_chain_error)?;
|
||||
let next_epoch = current_epoch + 1;
|
||||
|
||||
if request_epoch > next_epoch {
|
||||
// Determine what the current epoch would be if we fast-forward our system clock by
|
||||
// `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
||||
//
|
||||
// Most of the time, `tolerant_current_epoch` will be equal to `current_epoch`. However, during
|
||||
// the first `MAXIMUM_GOSSIP_CLOCK_DISPARITY` duration of the epoch `tolerant_current_epoch`
|
||||
// will equal `current_epoch + 1`
|
||||
let tolerant_current_epoch = chain
|
||||
.slot_clock
|
||||
.now_with_future_tolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY)
|
||||
.ok_or_else(|| warp_utils::reject::custom_server_error("unable to read slot clock".into()))?
|
||||
.epoch(T::EthSpec::slots_per_epoch());
|
||||
|
||||
if request_epoch == current_epoch
|
||||
|| request_epoch == tolerant_current_epoch
|
||||
|| request_epoch == current_epoch + 1
|
||||
|| request_epoch == tolerant_current_epoch + 1
|
||||
{
|
||||
cached_attestation_duties(request_epoch, request_indices, chain)
|
||||
} else if request_epoch > current_epoch + 1 {
|
||||
Err(warp_utils::reject::custom_bad_request(format!(
|
||||
"request epoch {} is more than one epoch past the current epoch {}",
|
||||
request_epoch, current_epoch
|
||||
)))
|
||||
} else if request_epoch == current_epoch || request_epoch == next_epoch {
|
||||
cached_attestation_duties(request_epoch, request_indices, chain)
|
||||
} else {
|
||||
// request_epoch < current_epoch
|
||||
compute_historic_attester_duties(request_epoch, request_indices, chain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
//! Contains the handler for the `GET validator/duties/proposer/{epoch}` endpoint.
|
||||
|
||||
use crate::state_id::StateId;
|
||||
use beacon_chain::{BeaconChain, BeaconChainError, BeaconChainTypes};
|
||||
use beacon_chain::{
|
||||
BeaconChain, BeaconChainError, BeaconChainTypes, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
|
||||
};
|
||||
use eth2::types::{self as api_types};
|
||||
use slog::{debug, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::state_advance::partial_state_advance;
|
||||
use std::cmp::Ordering;
|
||||
use types::{BeaconState, ChainSpec, CloneConfig, Epoch, EthSpec, Hash256, Slot};
|
||||
@@ -21,35 +24,43 @@ pub fn proposer_duties<T: BeaconChainTypes>(
|
||||
.epoch()
|
||||
.map_err(warp_utils::reject::beacon_chain_error)?;
|
||||
|
||||
match request_epoch.cmp(¤t_epoch) {
|
||||
// request_epoch > current_epoch
|
||||
//
|
||||
// Determine what the current epoch would be if we fast-forward our system clock by
|
||||
// `MAXIMUM_GOSSIP_CLOCK_DISPARITY`.
|
||||
//
|
||||
// Most of the time, `tolerant_current_epoch` will be equal to `current_epoch`. However, during
|
||||
// the first `MAXIMUM_GOSSIP_CLOCK_DISPARITY` duration of the epoch `tolerant_current_epoch`
|
||||
// will equal `current_epoch + 1`
|
||||
let tolerant_current_epoch = chain
|
||||
.slot_clock
|
||||
.now_with_future_tolerance(MAXIMUM_GOSSIP_CLOCK_DISPARITY)
|
||||
.ok_or_else(|| warp_utils::reject::custom_server_error("unable to read slot clock".into()))?
|
||||
.epoch(T::EthSpec::slots_per_epoch());
|
||||
|
||||
if request_epoch == current_epoch || request_epoch == tolerant_current_epoch {
|
||||
// If we could consider ourselves in the `request_epoch` when allowing for clock disparity
|
||||
// tolerance then serve this request from the cache.
|
||||
if let Some(duties) = try_proposer_duties_from_cache(request_epoch, chain)? {
|
||||
Ok(duties)
|
||||
} else {
|
||||
debug!(
|
||||
log,
|
||||
"Proposer cache miss";
|
||||
"request_epoch" => request_epoch,
|
||||
);
|
||||
compute_and_cache_proposer_duties(request_epoch, chain)
|
||||
}
|
||||
} else if request_epoch > current_epoch {
|
||||
// Reject queries about the future as they're very expensive there's no look-ahead for
|
||||
// proposer duties.
|
||||
Ordering::Greater => Err(warp_utils::reject::custom_bad_request(format!(
|
||||
Err(warp_utils::reject::custom_bad_request(format!(
|
||||
"request epoch {} is ahead of the current epoch {}",
|
||||
request_epoch, current_epoch
|
||||
))),
|
||||
// request_epoch == current_epoch
|
||||
//
|
||||
// Queries about the current epoch should attempt to find the value in the cache. If it
|
||||
// can't be found, it should be computed and then stored in the cache for future gains.
|
||||
Ordering::Equal => {
|
||||
if let Some(duties) = try_proposer_duties_from_cache(request_epoch, chain)? {
|
||||
Ok(duties)
|
||||
} else {
|
||||
debug!(
|
||||
log,
|
||||
"Proposer cache miss";
|
||||
"request_epoch" => request_epoch,
|
||||
);
|
||||
compute_and_cache_proposer_duties(request_epoch, chain)
|
||||
}
|
||||
}
|
||||
)))
|
||||
} else {
|
||||
// request_epoch < current_epoch
|
||||
//
|
||||
// Queries about the past are handled with a slow path.
|
||||
Ordering::Less => compute_historic_proposer_duties(request_epoch, chain),
|
||||
compute_historic_proposer_duties(request_epoch, chain)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,10 +69,10 @@ pub fn proposer_duties<T: BeaconChainTypes>(
|
||||
///
|
||||
/// ## Notes
|
||||
///
|
||||
/// The `current_epoch` value should equal the current epoch on the slot clock, otherwise we risk
|
||||
/// washing out the proposer cache at the expense of block processing.
|
||||
/// The `current_epoch` value should equal the current epoch on the slot clock (with some
|
||||
/// tolerance), otherwise we risk washing out the proposer cache at the expense of block processing.
|
||||
fn try_proposer_duties_from_cache<T: BeaconChainTypes>(
|
||||
current_epoch: Epoch,
|
||||
request_epoch: Epoch,
|
||||
chain: &BeaconChain<T>,
|
||||
) -> Result<Option<ApiDuties>, warp::reject::Rejection> {
|
||||
let head = chain
|
||||
@@ -69,16 +80,16 @@ fn try_proposer_duties_from_cache<T: BeaconChainTypes>(
|
||||
.map_err(warp_utils::reject::beacon_chain_error)?;
|
||||
let head_epoch = head.slot.epoch(T::EthSpec::slots_per_epoch());
|
||||
|
||||
let dependent_root = match head_epoch.cmp(¤t_epoch) {
|
||||
// head_epoch == current_epoch
|
||||
let dependent_root = match head_epoch.cmp(&request_epoch) {
|
||||
// head_epoch == request_epoch
|
||||
Ordering::Equal => head.proposer_shuffling_decision_root,
|
||||
// head_epoch < current_epoch
|
||||
// head_epoch < request_epoch
|
||||
Ordering::Less => head.block_root,
|
||||
// head_epoch > current_epoch
|
||||
// head_epoch > request_epoch
|
||||
Ordering::Greater => {
|
||||
return Err(warp_utils::reject::custom_server_error(format!(
|
||||
"head epoch {} is later than current epoch {}",
|
||||
head_epoch, current_epoch
|
||||
"head epoch {} is later than request epoch {}",
|
||||
head_epoch, request_epoch
|
||||
)))
|
||||
}
|
||||
};
|
||||
@@ -86,10 +97,10 @@ fn try_proposer_duties_from_cache<T: BeaconChainTypes>(
|
||||
chain
|
||||
.beacon_proposer_cache
|
||||
.lock()
|
||||
.get_epoch::<T::EthSpec>(dependent_root, current_epoch)
|
||||
.get_epoch::<T::EthSpec>(dependent_root, request_epoch)
|
||||
.cloned()
|
||||
.map(|indices| {
|
||||
convert_to_api_response(chain, current_epoch, dependent_root, indices.to_vec())
|
||||
convert_to_api_response(chain, request_epoch, dependent_root, indices.to_vec())
|
||||
})
|
||||
.transpose()
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use beacon_chain::{
|
||||
test_utils::{AttestationStrategy, BeaconChainHarness, BlockStrategy, EphemeralHarnessType},
|
||||
BeaconChain, StateSkipConfig,
|
||||
BeaconChain, StateSkipConfig, MAXIMUM_GOSSIP_CLOCK_DISPARITY,
|
||||
};
|
||||
use discv5::enr::{CombinedKey, EnrBuilder};
|
||||
use environment::null_logger;
|
||||
@@ -18,6 +18,7 @@ use futures::stream::{Stream, StreamExt};
|
||||
use futures::FutureExt;
|
||||
use http_api::{Config, Context};
|
||||
use network::NetworkMessage;
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::per_slot_processing;
|
||||
use std::convert::TryInto;
|
||||
use std::iter::Iterator;
|
||||
@@ -1682,6 +1683,57 @@ impl ApiTester {
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn test_get_validator_duties_early(self) -> Self {
|
||||
let current_epoch = self.chain.epoch().unwrap();
|
||||
let next_epoch = current_epoch + 1;
|
||||
let current_epoch_start = self
|
||||
.chain
|
||||
.slot_clock
|
||||
.start_of(current_epoch.start_slot(E::slots_per_epoch()))
|
||||
.unwrap();
|
||||
|
||||
self.chain.slot_clock.set_current_time(
|
||||
current_epoch_start - MAXIMUM_GOSSIP_CLOCK_DISPARITY - Duration::from_millis(1),
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
self.client
|
||||
.get_validator_duties_proposer(current_epoch)
|
||||
.await
|
||||
.unwrap_err()
|
||||
.status()
|
||||
.map(Into::into),
|
||||
Some(400),
|
||||
"should not get proposer duties outside of tolerance"
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
self.client
|
||||
.post_validator_duties_attester(next_epoch, &[0])
|
||||
.await
|
||||
.unwrap_err()
|
||||
.status()
|
||||
.map(Into::into),
|
||||
Some(400),
|
||||
"should not get attester duties outside of tolerance"
|
||||
);
|
||||
|
||||
self.chain
|
||||
.slot_clock
|
||||
.set_current_time(current_epoch_start - MAXIMUM_GOSSIP_CLOCK_DISPARITY);
|
||||
|
||||
self.client
|
||||
.get_validator_duties_proposer(current_epoch)
|
||||
.await
|
||||
.expect("should get proposer duties within tolerance");
|
||||
self.client
|
||||
.post_validator_duties_attester(next_epoch, &[0])
|
||||
.await
|
||||
.expect("should get attester duties within tolerance");
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn test_block_production(self) -> Self {
|
||||
let fork = self.chain.head_info().unwrap().fork;
|
||||
let genesis_validators_root = self.chain.genesis_validators_root;
|
||||
@@ -2356,6 +2408,11 @@ async fn node_get() {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn get_validator_duties_early() {
|
||||
ApiTester::new().test_get_validator_duties_early().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn get_validator_duties_attester() {
|
||||
ApiTester::new().test_get_validator_duties_attester().await;
|
||||
|
||||
@@ -10,7 +10,7 @@ use std::cmp::max;
|
||||
use std::fs;
|
||||
use std::net::{IpAddr, Ipv4Addr, ToSocketAddrs};
|
||||
use std::net::{TcpListener, UdpSocket};
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use types::{ChainSpec, Checkpoint, Epoch, EthSpec, Hash256, PublicKeyBytes, GRAFFITI_BYTES_LEN};
|
||||
|
||||
@@ -422,7 +422,7 @@ pub fn get_config<E: EthSpec>(
|
||||
pub fn set_network_config(
|
||||
config: &mut NetworkConfig,
|
||||
cli_args: &ArgMatches,
|
||||
data_dir: &PathBuf,
|
||||
data_dir: &Path,
|
||||
log: &Logger,
|
||||
use_listening_port_as_enr_port_by_default: bool,
|
||||
) -> Result<(), String> {
|
||||
|
||||
@@ -660,7 +660,7 @@ impl<E: EthSpec, Hot: ItemStore<E>, Cold: ItemStore<E>> HotColdDB<E, Hot, Cold>
|
||||
partial_state.load_historical_roots(&self.cold_db, &self.spec)?;
|
||||
partial_state.load_randao_mixes(&self.cold_db, &self.spec)?;
|
||||
|
||||
Ok(partial_state.try_into()?)
|
||||
partial_state.try_into()
|
||||
}
|
||||
|
||||
/// Load a restore point state by its `restore_point_index`.
|
||||
|
||||
@@ -4,14 +4,15 @@ Each Lighthouse release contains several downloadable binaries in the "Assets"
|
||||
section of the release. You can find the [releases
|
||||
on Github](https://github.com/sigp/lighthouse/releases).
|
||||
|
||||
> Note: binaries are not yet provided for MacOS or Windows native.
|
||||
> Note: binaries are not yet provided for Windows native.
|
||||
|
||||
## Platforms
|
||||
|
||||
Binaries are supplied for two platforms:
|
||||
Binaries are supplied for three platforms:
|
||||
|
||||
- `x86_64-unknown-linux-gnu`: AMD/Intel 64-bit processors (most desktops, laptops, servers)
|
||||
- `aarch64-unknown-linux-gnu`: 64-bit ARM processors (Raspberry Pi 4)
|
||||
- `x86_64-apple-darwin`: macOS with Intel chips
|
||||
|
||||
Additionally there is also a `-portable` suffix which indicates if the `portable` feature is used:
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ In order to initiate an exit, users can use the `lighthouse account validator ex
|
||||
|
||||
- The `--keystore` flag is used to specify the path to the EIP-2335 voting keystore for the validator.
|
||||
|
||||
- The `--beacon-nodes` flag is used to specify a beacon chain HTTP endpoint that confirms to the [Eth2.0 Standard API](https://ethereum.github.io/eth2.0-APIs/) specifications. That beacon node will be used to validate and propagate the voluntary exit. The default value for this flag is `http://localhost:5052`.
|
||||
- The `--beacon-node` flag is used to specify a beacon chain HTTP endpoint that confirms to the [Eth2.0 Standard API](https://ethereum.github.io/eth2.0-APIs/) specifications. That beacon node will be used to validate and propagate the voluntary exit. The default value for this flag is `http://localhost:5052`.
|
||||
|
||||
- The `--network` flag is used to specify a particular Eth2 network (default is `mainnet`).
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "boot_node"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
authors = ["Sigma Prime <contact@sigmaprime.io>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ fn testnet_url() -> String {
|
||||
fn main() {
|
||||
match get_all_contracts() {
|
||||
Ok(()) => (),
|
||||
Err(e) => panic!(e),
|
||||
Err(e) => panic!("{}", e),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ pub fn decode_eth1_tx_data(
|
||||
)
|
||||
.map_err(DecodeError::SszDecodeError)?
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
let root = decode_token!(Hash256, to_fixed_bytes);
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ pub const VERSION: &str = git_version!(
|
||||
// NOTE: using --match instead of --exclude for compatibility with old Git
|
||||
"--match=thiswillnevermatchlol"
|
||||
],
|
||||
prefix = "Lighthouse/v1.2.1-",
|
||||
prefix = "Lighthouse/v1.2.2-",
|
||||
fallback = "unknown"
|
||||
);
|
||||
|
||||
@@ -38,6 +38,10 @@ mod test {
|
||||
fn version_formatting() {
|
||||
let re = Regex::new(r"^Lighthouse/v[0-9]+\.[0-9]+\.[0-9]+(-rc.[0-9])?-[[:xdigit:]]{7}\+?$")
|
||||
.unwrap();
|
||||
assert!(re.is_match(VERSION), VERSION);
|
||||
assert!(
|
||||
re.is_match(VERSION),
|
||||
"version doesn't match regex: {}",
|
||||
VERSION
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +149,7 @@ mod post {
|
||||
let r = run_testcase(u).unwrap_err();
|
||||
|
||||
for msg in msgs.iter() {
|
||||
assert!(r.contains(msg), format!("{:?} should contain {:?}", r, msg));
|
||||
assert!(r.contains(msg), "{:?} should contain {:?}", r, msg);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ impl ValidatorDir {
|
||||
}
|
||||
|
||||
/// Returns the `dir` provided to `Self::open`.
|
||||
pub fn dir(&self) -> &PathBuf {
|
||||
pub fn dir(&self) -> &Path {
|
||||
&self.dir
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ impl ValidatorDir {
|
||||
|
||||
/// Attempts to load and decrypt a Keypair given path to the keystore.
|
||||
pub fn unlock_keypair<P: AsRef<Path>>(
|
||||
keystore_path: &PathBuf,
|
||||
keystore_path: &Path,
|
||||
password_dir: P,
|
||||
) -> Result<Keypair, Error> {
|
||||
let keystore = Keystore::from_json_reader(
|
||||
@@ -229,8 +229,8 @@ pub fn unlock_keypair<P: AsRef<Path>>(
|
||||
|
||||
/// Attempts to load and decrypt a Keypair given path to the keystore and the password file.
|
||||
pub fn unlock_keypair_from_password_path(
|
||||
keystore_path: &PathBuf,
|
||||
password_path: &PathBuf,
|
||||
keystore_path: &Path,
|
||||
password_path: &Path,
|
||||
) -> Result<Keypair, Error> {
|
||||
let keystore = Keystore::from_json_reader(
|
||||
&mut OpenOptions::new()
|
||||
@@ -242,7 +242,7 @@ pub fn unlock_keypair_from_password_path(
|
||||
.map_err(Error::UnableToReadKeystore)?;
|
||||
|
||||
let password: PlainText = read(password_path)
|
||||
.map_err(|_| Error::UnableToReadPassword(password_path.clone()))?
|
||||
.map_err(|_| Error::UnableToReadPassword(password_path.into()))?
|
||||
.into();
|
||||
keystore
|
||||
.decrypt_keypair(password.as_bytes())
|
||||
|
||||
@@ -182,7 +182,7 @@ fn concurrency() {
|
||||
let harness = Harness::new();
|
||||
|
||||
let val_dir = harness.create_and_test(&BuildConfig::default());
|
||||
let path = val_dir.dir().clone();
|
||||
let path = val_dir.dir().to_owned();
|
||||
|
||||
// Should not re-open whilst opened after build.
|
||||
ValidatorDir::open(&path).unwrap_err();
|
||||
|
||||
@@ -55,8 +55,8 @@ impl<'de> Visitor<'de> for HexVisitor {
|
||||
where
|
||||
E: de::Error,
|
||||
{
|
||||
Ok(hex::decode(value.trim_start_matches("0x"))
|
||||
.map_err(|e| de::Error::custom(format!("invalid hex ({:?})", e)))?)
|
||||
hex::decode(value.trim_start_matches("0x"))
|
||||
.map_err(|e| de::Error::custom(format!("invalid hex ({:?})", e)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -353,7 +353,7 @@ macro_rules! impl_decodable_for_u8_array {
|
||||
Err(DecodeError::InvalidByteLength { len, expected })
|
||||
} else {
|
||||
let mut array: [u8; $len] = [0; $len];
|
||||
array.copy_from_slice(&bytes[..]);
|
||||
array.copy_from_slice(bytes);
|
||||
|
||||
Ok(array)
|
||||
}
|
||||
|
||||
@@ -108,16 +108,16 @@ impl<T: Default, N: Unsigned> From<Vec<T>> for FixedVector<T, N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, N: Unsigned> Into<Vec<T>> for FixedVector<T, N> {
|
||||
fn into(self) -> Vec<T> {
|
||||
self.vec
|
||||
impl<T, N: Unsigned> From<FixedVector<T, N>> for Vec<T> {
|
||||
fn from(vector: FixedVector<T, N>) -> Vec<T> {
|
||||
vector.vec
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, N: Unsigned> Default for FixedVector<T, N> {
|
||||
impl<T: Default, N: Unsigned> Default for FixedVector<T, N> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
vec: Vec::default(),
|
||||
vec: (0..N::to_usize()).map(|_| T::default()).collect(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,9 +120,9 @@ impl<T, N: Unsigned> From<Vec<T>> for VariableList<T, N> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, N: Unsigned> Into<Vec<T>> for VariableList<T, N> {
|
||||
fn into(self) -> Vec<T> {
|
||||
self.vec
|
||||
impl<T, N: Unsigned> From<VariableList<T, N>> for Vec<T> {
|
||||
fn from(list: VariableList<T, N>) -> Vec<T> {
|
||||
list.vec
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
use crate::*;
|
||||
use int_to_bytes::int_to_bytes4;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::File;
|
||||
use std::path::Path;
|
||||
use tree_hash::TreeHash;
|
||||
@@ -449,10 +450,8 @@ mod tests {
|
||||
}
|
||||
|
||||
/// YAML config file as defined by the spec.
|
||||
///
|
||||
/// Spec v0.12.3
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
|
||||
#[serde(rename_all = "UPPERCASE", deny_unknown_fields)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub struct YamlConfig {
|
||||
pub config_name: String,
|
||||
// ChainSpec
|
||||
@@ -576,6 +575,10 @@ pub struct YamlConfig {
|
||||
#[serde(with = "serde_utils::quoted_u64")]
|
||||
deposit_network_id: u64,
|
||||
deposit_contract_address: Address,
|
||||
|
||||
// Extra fields (could be from a future hard-fork that we don't yet know).
|
||||
#[serde(flatten)]
|
||||
pub extra_fields: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Default for YamlConfig {
|
||||
@@ -671,6 +674,8 @@ impl YamlConfig {
|
||||
deposit_chain_id: spec.deposit_chain_id,
|
||||
deposit_network_id: spec.deposit_network_id,
|
||||
deposit_contract_address: spec.deposit_contract_address,
|
||||
|
||||
extra_fields: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,6 +854,31 @@ mod yaml_tests {
|
||||
assert_eq!(from, yamlconfig);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extra_fields_round_trip() {
|
||||
let tmp_file = NamedTempFile::new().expect("failed to create temp file");
|
||||
let writer = OpenOptions::new()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.open(tmp_file.as_ref())
|
||||
.expect("error opening file");
|
||||
let mainnet_spec = ChainSpec::mainnet();
|
||||
let mut yamlconfig = YamlConfig::from_spec::<MainnetEthSpec>(&mainnet_spec);
|
||||
let (k1, v1) = ("SAMPLE_HARDFORK_KEY1", "123456789");
|
||||
let (k2, v2) = ("SAMPLE_HARDFORK_KEY2", "987654321");
|
||||
yamlconfig.extra_fields.insert(k1.into(), v1.into());
|
||||
yamlconfig.extra_fields.insert(k2.into(), v2.into());
|
||||
serde_yaml::to_writer(writer, &yamlconfig).expect("failed to write or serialize");
|
||||
|
||||
let reader = OpenOptions::new()
|
||||
.read(true)
|
||||
.write(false)
|
||||
.open(tmp_file.as_ref())
|
||||
.expect("error while opening the file");
|
||||
let from: YamlConfig = serde_yaml::from_reader(reader).expect("error while deserializing");
|
||||
assert_eq!(from, yamlconfig);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn apply_to_spec() {
|
||||
let mut spec = ChainSpec::minimal();
|
||||
|
||||
@@ -19,35 +19,35 @@ where
|
||||
aggregate: Cow<'a, GenericAggregateSignature<Pub, AggPub, Sig, AggSig>>,
|
||||
}
|
||||
|
||||
impl<'a, Pub, AggPub, Sig, AggSig> Into<WrappedSignature<'a, Pub, AggPub, Sig, AggSig>>
|
||||
for &'a GenericSignature<Pub, Sig>
|
||||
impl<'a, Pub, AggPub, Sig, AggSig> From<&'a GenericSignature<Pub, Sig>>
|
||||
for WrappedSignature<'a, Pub, AggPub, Sig, AggSig>
|
||||
where
|
||||
Pub: TPublicKey + Clone,
|
||||
AggPub: Clone,
|
||||
Sig: TSignature<Pub> + Clone,
|
||||
AggSig: TAggregateSignature<Pub, AggPub, Sig> + Clone,
|
||||
{
|
||||
fn into(self) -> WrappedSignature<'a, Pub, AggPub, Sig, AggSig> {
|
||||
fn from(sig: &'a GenericSignature<Pub, Sig>) -> Self {
|
||||
let mut aggregate: GenericAggregateSignature<Pub, AggPub, Sig, AggSig> =
|
||||
GenericAggregateSignature::infinity();
|
||||
aggregate.add_assign(self);
|
||||
aggregate.add_assign(sig);
|
||||
WrappedSignature {
|
||||
aggregate: Cow::Owned(aggregate),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Pub, AggPub, Sig, AggSig> Into<WrappedSignature<'a, Pub, AggPub, Sig, AggSig>>
|
||||
for &'a GenericAggregateSignature<Pub, AggPub, Sig, AggSig>
|
||||
impl<'a, Pub, AggPub, Sig, AggSig> From<&'a GenericAggregateSignature<Pub, AggPub, Sig, AggSig>>
|
||||
for WrappedSignature<'a, Pub, AggPub, Sig, AggSig>
|
||||
where
|
||||
Pub: TPublicKey + Clone,
|
||||
AggPub: Clone,
|
||||
Sig: Clone,
|
||||
AggSig: Clone,
|
||||
{
|
||||
fn into(self) -> WrappedSignature<'a, Pub, AggPub, Sig, AggSig> {
|
||||
fn from(aggregate: &'a GenericAggregateSignature<Pub, AggPub, Sig, AggSig>) -> Self {
|
||||
WrappedSignature {
|
||||
aggregate: Cow::Borrowed(self),
|
||||
aggregate: Cow::Borrowed(aggregate),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "lcli"
|
||||
description = "Lighthouse CLI (modeled after zcli)"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
authors = ["Paul Hauner <paul@paulhauner.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lighthouse"
|
||||
version = "1.2.1"
|
||||
version = "1.2.2"
|
||||
authors = ["Sigma Prime <contact@sigmaprime.io>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#![cfg(feature = "ef_tests")]
|
||||
|
||||
use ef_tests::*;
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use types::*;
|
||||
|
||||
@@ -17,6 +18,11 @@ fn config_test<E: EthSpec + TypeName>() {
|
||||
let yaml_from_spec = YamlConfig::from_spec::<E>(&spec);
|
||||
assert_eq!(yaml_config.apply_to_chain_spec::<E>(&spec), Some(spec));
|
||||
assert_eq!(yaml_from_spec, yaml_config);
|
||||
assert_eq!(
|
||||
yaml_config.extra_fields,
|
||||
HashMap::new(),
|
||||
"not all config fields read"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -7,7 +7,7 @@ use state_processing::test_utils::BlockProcessingBuilder;
|
||||
use std::env;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::exit;
|
||||
use types::MainnetEthSpec;
|
||||
use types::{BeaconState, ChainSpec, EthSpec, SignedBeaconBlock};
|
||||
@@ -91,12 +91,12 @@ fn write_vectors_to_file(title: &str, vectors: &[TestVector]) -> Result<(), Stri
|
||||
}
|
||||
|
||||
/// Write some SSZ object to file.
|
||||
fn write_to_ssz_file<T: Encode>(path: &PathBuf, item: &T) -> Result<(), String> {
|
||||
fn write_to_ssz_file<T: Encode>(path: &Path, item: &T) -> Result<(), String> {
|
||||
write_to_file(path, &item.as_ssz_bytes())
|
||||
}
|
||||
|
||||
/// Write some bytes to file.
|
||||
fn write_to_file(path: &PathBuf, item: &[u8]) -> Result<(), String> {
|
||||
fn write_to_file(path: &Path, item: &[u8]) -> Result<(), String> {
|
||||
File::create(path)
|
||||
.map_err(|e| format!("Unable to create {:?}: {:?}", path, e))
|
||||
.and_then(|mut file| {
|
||||
|
||||
@@ -7,7 +7,7 @@ use crate::http_metrics::metrics::{inc_counter_vec, ENDPOINT_ERRORS, ENDPOINT_RE
|
||||
use environment::RuntimeContext;
|
||||
use eth2::BeaconNodeHttpClient;
|
||||
use futures::future;
|
||||
use slog::{error, info, warn, Logger};
|
||||
use slog::{debug, error, info, warn, Logger};
|
||||
use slot_clock::SlotClock;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
@@ -236,6 +236,14 @@ impl<E: EthSpec> CandidateBeaconNode<E> {
|
||||
CandidateError::Incompatible
|
||||
})?;
|
||||
|
||||
if !yaml_config.extra_fields.is_empty() {
|
||||
debug!(
|
||||
log,
|
||||
"Beacon spec includes unknown fields";
|
||||
"fields" => ?yaml_config.extra_fields
|
||||
);
|
||||
}
|
||||
|
||||
if *spec == beacon_node_spec {
|
||||
Ok(())
|
||||
} else {
|
||||
|
||||
@@ -19,7 +19,7 @@ use slog::{debug, error, info, warn, Logger};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use types::{Graffiti, Keypair, PublicKey, PublicKeyBytes};
|
||||
|
||||
use crate::key_cache;
|
||||
@@ -101,20 +101,16 @@ impl InitializedValidator {
|
||||
}
|
||||
}
|
||||
|
||||
fn open_keystore(path: &PathBuf) -> Result<Keystore, Error> {
|
||||
fn open_keystore(path: &Path) -> Result<Keystore, Error> {
|
||||
let keystore_file = File::open(path).map_err(Error::UnableToOpenVotingKeystore)?;
|
||||
Keystore::from_json_reader(keystore_file).map_err(Error::UnableToParseVotingKeystore)
|
||||
}
|
||||
|
||||
fn get_lockfile_path(file_path: &PathBuf) -> Option<PathBuf> {
|
||||
fn get_lockfile_path(file_path: &Path) -> Option<PathBuf> {
|
||||
file_path
|
||||
.file_name()
|
||||
.and_then(|os_str| os_str.to_str())
|
||||
.map(|filename| {
|
||||
file_path
|
||||
.clone()
|
||||
.with_file_name(format!("{}.lock", filename))
|
||||
})
|
||||
.map(|filename| file_path.with_file_name(format!("{}.lock", filename)))
|
||||
}
|
||||
|
||||
impl InitializedValidator {
|
||||
@@ -238,7 +234,7 @@ impl InitializedValidator {
|
||||
/// Try to unlock `keystore` at `keystore_path` by prompting the user via `stdin`.
|
||||
fn unlock_keystore_via_stdin_password(
|
||||
keystore: &Keystore,
|
||||
keystore_path: &PathBuf,
|
||||
keystore_path: &Path,
|
||||
) -> Result<(ZeroizeString, Keypair), Error> {
|
||||
eprintln!();
|
||||
eprintln!(
|
||||
|
||||
Reference in New Issue
Block a user