Adds ENR "eth2" field and Fork logic to networking (#953)

* Merge #913

* Correct release tests

* Completed release test corrections

* Initial work on upgrading discovery

* Updates discovery to latest version

* Update ENR initialisation logic

* Remove debug statements

* Shifts timing units to slots

* Initial work

* Add initial fork versioning and EnrForkId

* Correct linking for EnrForkId

* Adds eth2 field to local ENR

* Initial work to eth2 field integration

* Integrate eth2 field into discovery

* temp commit

* Add a timer to adjust fork versions during a hard fork for the ENR
This commit is contained in:
Age Manning 2020-03-24 21:45:53 +11:00 committed by GitHub
parent af1c5c326c
commit 58111cddb2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 431 additions and 235 deletions

55
Cargo.lock generated
View File

@ -234,6 +234,7 @@ dependencies = [
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive", "eth2_ssz_derive",
"eth2_ssz_types", "eth2_ssz_types",
"fork",
"futures", "futures",
"genesis", "genesis",
"integer-sqrt", "integer-sqrt",
@ -1089,7 +1090,7 @@ dependencies = [
[[package]] [[package]]
name = "enr" name = "enr"
version = "0.1.0-alpha.3" version = "0.1.0-alpha.3"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"base64 0.12.0", "base64 0.12.0",
"bs58 0.3.0", "bs58 0.3.0",
@ -2057,7 +2058,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p" name = "libp2p"
version = "0.13.2" version = "0.13.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2096,7 +2097,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-core" name = "libp2p-core"
version = "0.13.2" version = "0.13.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"asn1_der", "asn1_der",
"bs58 0.3.0", "bs58 0.3.0",
@ -2131,7 +2132,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-core-derive" name = "libp2p-core-derive"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"quote 1.0.3", "quote 1.0.3",
"syn 1.0.16", "syn 1.0.16",
@ -2140,7 +2141,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-deflate" name = "libp2p-deflate"
version = "0.5.0" version = "0.5.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"flate2", "flate2",
"futures", "futures",
@ -2151,7 +2152,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-discv5" name = "libp2p-discv5"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"arrayvec 0.4.12", "arrayvec 0.4.12",
"bigint", "bigint",
@ -2182,7 +2183,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-dns" name = "libp2p-dns"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"futures", "futures",
"libp2p-core", "libp2p-core",
@ -2193,7 +2194,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-floodsub" name = "libp2p-floodsub"
version = "0.13.1" version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bs58 0.3.0", "bs58 0.3.0",
"bytes", "bytes",
@ -2211,7 +2212,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-gossipsub" name = "libp2p-gossipsub"
version = "0.1.0" version = "0.1.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"base64 0.10.1", "base64 0.10.1",
"bs58 0.2.5", "bs58 0.2.5",
@ -2236,7 +2237,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-identify" name = "libp2p-identify"
version = "0.13.2" version = "0.13.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2255,7 +2256,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-kad" name = "libp2p-kad"
version = "0.13.2" version = "0.13.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"arrayvec 0.5.1", "arrayvec 0.5.1",
"bytes", "bytes",
@ -2282,7 +2283,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-mdns" name = "libp2p-mdns"
version = "0.13.1" version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"data-encoding", "data-encoding",
"dns-parser", "dns-parser",
@ -2304,7 +2305,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-mplex" name = "libp2p-mplex"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -2320,7 +2321,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-noise" name = "libp2p-noise"
version = "0.11.1" version = "0.11.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"curve25519-dalek 1.2.3", "curve25519-dalek 1.2.3",
@ -2340,7 +2341,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-ping" name = "libp2p-ping"
version = "0.13.1" version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2357,7 +2358,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-plaintext" name = "libp2p-plaintext"
version = "0.13.1" version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2372,7 +2373,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-secio" name = "libp2p-secio"
version = "0.13.1" version = "0.13.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"aes-ctr", "aes-ctr",
"bytes", "bytes",
@ -2401,7 +2402,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-swarm" name = "libp2p-swarm"
version = "0.3.0" version = "0.3.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"futures", "futures",
"libp2p-core", "libp2p-core",
@ -2414,7 +2415,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-tcp" name = "libp2p-tcp"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2430,7 +2431,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-uds" name = "libp2p-uds"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"futures", "futures",
"libp2p-core", "libp2p-core",
@ -2441,7 +2442,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-wasm-ext" name = "libp2p-wasm-ext"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"futures", "futures",
"js-sys", "js-sys",
@ -2455,7 +2456,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-websocket" name = "libp2p-websocket"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2473,7 +2474,7 @@ dependencies = [
[[package]] [[package]]
name = "libp2p-yamux" name = "libp2p-yamux"
version = "0.13.0" version = "0.13.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"futures", "futures",
"libp2p-core", "libp2p-core",
@ -2773,7 +2774,7 @@ dependencies = [
[[package]] [[package]]
name = "multistream-select" name = "multistream-select"
version = "0.6.1" version = "0.6.1"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -3006,7 +3007,7 @@ dependencies = [
[[package]] [[package]]
name = "parity-multiaddr" name = "parity-multiaddr"
version = "0.6.0" version = "0.6.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"bs58 0.3.0", "bs58 0.3.0",
@ -3023,7 +3024,7 @@ dependencies = [
[[package]] [[package]]
name = "parity-multihash" name = "parity-multihash"
version = "0.2.0" version = "0.2.0"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"blake2", "blake2",
"bytes", "bytes",
@ -3799,7 +3800,7 @@ dependencies = [
[[package]] [[package]]
name = "rw-stream-sink" name = "rw-stream-sink"
version = "0.1.2" version = "0.1.2"
source = "git+https://github.com/SigP/rust-libp2p?rev=0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6#0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" source = "git+https://github.com/SigP/rust-libp2p?rev=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",

View File

@ -12,6 +12,7 @@ write_ssz_files = [] # Writes debugging .ssz files to /tmp during block process
eth2_config = { path = "../../eth2/utils/eth2_config" } eth2_config = { path = "../../eth2/utils/eth2_config" }
merkle_proof = { path = "../../eth2/utils/merkle_proof" } merkle_proof = { path = "../../eth2/utils/merkle_proof" }
store = { path = "../store" } store = { path = "../store" }
fork = { path = "../fork" }
parking_lot = "0.9.0" parking_lot = "0.9.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
lighthouse_metrics = { path = "../../eth2/utils/lighthouse_metrics" } lighthouse_metrics = { path = "../../eth2/utils/lighthouse_metrics" }

View File

@ -14,6 +14,7 @@ use crate::snapshot_cache::SnapshotCache;
use crate::timeout_rw_lock::TimeoutRwLock; use crate::timeout_rw_lock::TimeoutRwLock;
use crate::validator_pubkey_cache::ValidatorPubkeyCache; use crate::validator_pubkey_cache::ValidatorPubkeyCache;
use crate::BeaconSnapshot; use crate::BeaconSnapshot;
use ::fork::{next_fork_epoch, next_fork_version};
use operation_pool::{OperationPool, PersistedOperationPool}; use operation_pool::{OperationPool, PersistedOperationPool};
use slog::{crit, debug, error, info, trace, warn, Logger}; use slog::{crit, debug, error, info, trace, warn, Logger};
use slot_clock::SlotClock; use slot_clock::SlotClock;
@ -160,6 +161,8 @@ pub struct BeaconChain<T: BeaconChainTypes> {
pub(crate) shuffling_cache: TimeoutRwLock<ShufflingCache>, pub(crate) shuffling_cache: TimeoutRwLock<ShufflingCache>,
/// Caches a map of `validator_index -> validator_pubkey`. /// Caches a map of `validator_index -> validator_pubkey`.
pub(crate) validator_pubkey_cache: TimeoutRwLock<ValidatorPubkeyCache>, pub(crate) validator_pubkey_cache: TimeoutRwLock<ValidatorPubkeyCache>,
/// A list of any hard-coded forks that have been disabled.
pub disabled_forks: Vec<String>,
/// Logging to CLI, etc. /// Logging to CLI, etc.
pub(crate) log: Logger, pub(crate) log: Logger,
} }
@ -2044,6 +2047,54 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
Ok(dump) Ok(dump)
} }
/// Gets the current EnrForkId.
///
/// v0.11
pub fn enr_fork_id(&self) -> Result<EnrForkId, Error> {
Ok(EnrForkId {
// TODO: To be implemented with v0.11 updates
fork_digest: [0, 0, 0, 0],
next_fork_version: next_fork_version(self.slot()?, &self.disabled_forks),
next_fork_epoch: next_fork_epoch::<T::EthSpec>(
&self.spec,
self.slot()?,
&self.disabled_forks,
),
})
}
/// Calculates the duration (in millis) to the next fork, if one exists.
///
/// This is required by the network thread to instantiate timeouts to update networking
/// constants
pub fn duration_to_next_fork(&self) -> Result<Option<tokio::timer::Delay>, Error> {
let current_slot = self.slot()?;
let next_fork_epoch =
next_fork_epoch::<T::EthSpec>(&self.spec, current_slot, &self.disabled_forks);
if next_fork_epoch != self.spec.far_future_epoch {
// There is an upcoming fork
let current_epoch = self.slot()?.epoch(T::EthSpec::slots_per_epoch());
let epochs_until_fork = next_fork_epoch
.saturating_sub(current_epoch)
.saturating_sub(1u64);
let millis_until_fork = T::EthSpec::slots_per_epoch()
* self.spec.milliseconds_per_slot
* epochs_until_fork.as_u64();
Ok(Some(tokio::timer::Delay::new(
Instant::now()
+ self
.slot_clock
.duration_to_next_epoch(T::EthSpec::slots_per_epoch())
.unwrap_or_else(|| Duration::from_secs(0))
+ Duration::from_millis(millis_until_fork)
// add a short timeout to start within the new fork period
+ Duration::from_millis(200),
)))
} else {
Ok(None)
}
}
} }
impl<T: BeaconChainTypes> Drop for BeaconChain<T> { impl<T: BeaconChainTypes> Drop for BeaconChain<T> {

View File

@ -87,6 +87,7 @@ pub struct BeaconChainBuilder<T: BeaconChainTypes> {
pubkey_cache_path: Option<PathBuf>, pubkey_cache_path: Option<PathBuf>,
validator_pubkey_cache: Option<ValidatorPubkeyCache>, validator_pubkey_cache: Option<ValidatorPubkeyCache>,
spec: ChainSpec, spec: ChainSpec,
disabled_forks: Vec<String>,
log: Option<Logger>, log: Option<Logger>,
} }
@ -121,6 +122,7 @@ where
head_tracker: None, head_tracker: None,
pubkey_cache_path: None, pubkey_cache_path: None,
data_dir: None, data_dir: None,
disabled_forks: Vec::new(),
validator_pubkey_cache: None, validator_pubkey_cache: None,
spec: TEthSpec::default_spec(), spec: TEthSpec::default_spec(),
log: None, log: None,
@ -167,6 +169,12 @@ where
self self
} }
/// Sets a list of hard-coded forks that will not be activated.
pub fn disabled_forks(mut self, disabled_forks: Vec<String>) -> Self {
self.disabled_forks = disabled_forks;
self
}
/// Attempt to load an existing eth1 cache from the builder's `Store`. /// Attempt to load an existing eth1 cache from the builder's `Store`.
pub fn get_persisted_eth1_backend(&self) -> Result<Option<SszEth1>, String> { pub fn get_persisted_eth1_backend(&self) -> Result<Option<SszEth1>, String> {
let store = self let store = self
@ -425,6 +433,7 @@ where
)), )),
shuffling_cache: TimeoutRwLock::new(ShufflingCache::new()), shuffling_cache: TimeoutRwLock::new(ShufflingCache::new()),
validator_pubkey_cache: TimeoutRwLock::new(validator_pubkey_cache), validator_pubkey_cache: TimeoutRwLock::new(validator_pubkey_cache),
disabled_forks: self.disabled_forks,
log: log.clone(), log: log.clone(),
}; };

View File

@ -125,6 +125,7 @@ where
let runtime_context = self.runtime_context.clone(); let runtime_context = self.runtime_context.clone();
let eth_spec_instance = self.eth_spec_instance.clone(); let eth_spec_instance = self.eth_spec_instance.clone();
let data_dir = config.data_dir.clone(); let data_dir = config.data_dir.clone();
let disabled_forks = config.disabled_forks.clone();
future::ok(()) future::ok(())
.and_then(move |()| { .and_then(move |()| {
@ -146,7 +147,8 @@ where
.store(store) .store(store)
.store_migrator(store_migrator) .store_migrator(store_migrator)
.data_dir(data_dir) .data_dir(data_dir)
.custom_spec(spec.clone()); .custom_spec(spec.clone())
.disabled_forks(disabled_forks);
Ok((builder, spec, context)) Ok((builder, spec, context))
}) })
@ -250,7 +252,7 @@ where
} }
/// Immediately starts the networking stack. /// Immediately starts the networking stack.
pub fn network(mut self, config: &NetworkConfig) -> Result<Self, String> { pub fn network(mut self, config: &mut NetworkConfig) -> Result<Self, String> {
let beacon_chain = self let beacon_chain = self
.beacon_chain .beacon_chain
.clone() .clone()

View File

@ -58,6 +58,8 @@ pub struct Config {
/// This is the method used for the 2019 client interop in Canada. /// This is the method used for the 2019 client interop in Canada.
pub dummy_eth1_backend: bool, pub dummy_eth1_backend: bool,
pub sync_eth1_chain: bool, pub sync_eth1_chain: bool,
/// A list of hard-coded forks that will be disabled.
pub disabled_forks: Vec<String>,
#[serde(skip)] #[serde(skip)]
/// The `genesis` field is not serialized or deserialized by `serde` to ensure it is defined /// The `genesis` field is not serialized or deserialized by `serde` to ensure it is defined
/// via the CLI at runtime, instead of from a configuration file saved to disk. /// via the CLI at runtime, instead of from a configuration file saved to disk.
@ -86,6 +88,7 @@ impl Default for Config {
dummy_eth1_backend: false, dummy_eth1_backend: false,
sync_eth1_chain: false, sync_eth1_chain: false,
eth1: <_>::default(), eth1: <_>::default(),
disabled_forks: Vec::new(),
} }
} }
} }

View File

@ -8,7 +8,7 @@ edition = "2018"
hex = "0.3" hex = "0.3"
# rust-libp2p is presently being sourced from a Sigma Prime fork of the # rust-libp2p is presently being sourced from a Sigma Prime fork of the
# `libp2p/rust-libp2p` repository. # `libp2p/rust-libp2p` repository.
libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "0cb9d504c7be6a7bcfc87feeafdb6847d8083fc6" } libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "8c272a9a4d115d9a1d33791479527cdcba781829" }
types = { path = "../../eth2/types" } types = { path = "../../eth2/types" }
serde = "1.0.102" serde = "1.0.102"
serde_derive = "1.0.102" serde_derive = "1.0.102"
@ -16,6 +16,7 @@ eth2_ssz = "0.1.2"
eth2_ssz_derive = "0.1.0" eth2_ssz_derive = "0.1.0"
slog = { version = "2.5.2", features = ["max_level_trace"] } slog = { version = "2.5.2", features = ["max_level_trace"] }
version = { path = "../version" } version = { path = "../version" }
# beacon_chain = { path = "../beacon_chain" }
tokio = "0.1.22" tokio = "0.1.22"
futures = "0.1.29" futures = "0.1.29"
error-chain = "0.12.1" error-chain = "0.12.1"

View File

@ -15,7 +15,7 @@ use libp2p::{
use lru::LruCache; use lru::LruCache;
use slog::{debug, o, warn}; use slog::{debug, o, warn};
use std::sync::Arc; use std::sync::Arc;
use types::EthSpec; use types::{EnrForkId, EthSpec};
const MAX_IDENTIFY_ADDRESSES: usize = 20; const MAX_IDENTIFY_ADDRESSES: usize = 20;
@ -87,6 +87,96 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
} }
} }
/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, TSpec> {
/* Pubsub behaviour functions */
/// Subscribes to a gossipsub topic.
pub fn subscribe(&mut self, topic: GossipTopic) -> bool {
if !self
.network_globals
.gossipsub_subscriptions
.read()
.contains(&topic)
{
self.network_globals
.gossipsub_subscriptions
.write()
.push(topic.clone());
}
self.gossipsub.subscribe(topic.into())
}
/// Unsubscribe from a gossipsub topic.
pub fn unsubscribe(&mut self, topic: GossipTopic) -> bool {
let pos = self
.network_globals
.gossipsub_subscriptions
.read()
.iter()
.position(|s| s == &topic);
if let Some(pos) = pos {
self.network_globals
.gossipsub_subscriptions
.write()
.swap_remove(pos);
}
self.gossipsub.unsubscribe(topic.into())
}
/// Publishes a list of messages on the pubsub (gossipsub) behaviour, choosing the encoding.
pub fn publish(&mut self, messages: Vec<PubsubMessage<TSpec>>) {
for message in messages {
for topic in message.topics() {
let message_data = message.encode();
self.gossipsub.publish(&topic.into(), message_data);
}
}
}
/// Forwards a message that is waiting in gossipsub's mcache. Messages are only propagated
/// once validated by the beacon chain.
pub fn propagate_message(&mut self, propagation_source: &PeerId, message_id: MessageId) {
self.gossipsub
.propagate_message(&message_id, propagation_source);
}
/* Eth2 RPC behaviour functions */
/// Sends an RPC Request/Response via the RPC protocol.
pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent<TSpec>) {
self.eth2_rpc.send_rpc(peer_id, rpc_event);
}
/* Discovery / Peer management functions */
/// Notify discovery that the peer has been banned.
pub fn peer_banned(&mut self, peer_id: PeerId) {
self.discovery.peer_banned(peer_id);
}
/// Notify discovery that the peer has been unbanned.
pub fn peer_unbanned(&mut self, peer_id: &PeerId) {
self.discovery.peer_unbanned(peer_id);
}
/// Returns an iterator over all enr entries in the DHT.
pub fn enr_entries(&mut self) -> impl Iterator<Item = &Enr> {
self.discovery.enr_entries()
}
/// Add an ENR to the routing table of the discovery mechanism.
pub fn add_enr(&mut self, enr: Enr) {
self.discovery.add_enr(enr);
}
/// Updates the local ENR's "eth2" field with the latest EnrForkId.
pub fn update_fork_version(&mut self, enr_fork_id: EnrForkId) {
self.discovery.update_eth2_enr(enr_fork_id);
// TODO: Handle gossipsub fork update
}
}
// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour // Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour
impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec>
NetworkBehaviourEventProcess<GossipsubEvent> for Behaviour<TSubstream, TSpec> NetworkBehaviourEventProcess<GossipsubEvent> for Behaviour<TSubstream, TSpec>
@ -194,95 +284,6 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> NetworkBehaviourEventPr
} }
} }
/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, TSpec> {
/* Pubsub behaviour functions */
/// Subscribes to a gossipsub topic.
pub fn subscribe(&mut self, topic: GossipTopic) -> bool {
if !self
.network_globals
.gossipsub_subscriptions
.read()
.contains(&topic)
{
self.network_globals
.gossipsub_subscriptions
.write()
.push(topic.clone());
}
self.gossipsub.subscribe(topic.into())
}
/// Unsubscribe from a gossipsub topic.
pub fn unsubscribe(&mut self, topic: GossipTopic) -> bool {
let pos = self
.network_globals
.gossipsub_subscriptions
.read()
.iter()
.position(|s| s == &topic);
if let Some(pos) = pos {
self.network_globals
.gossipsub_subscriptions
.write()
.swap_remove(pos);
}
self.gossipsub.unsubscribe(topic.into())
}
/// Publishes a list of messages on the pubsub (gossipsub) behaviour, choosing the encoding.
pub fn publish(&mut self, messages: Vec<PubsubMessage<TSpec>>) {
for message in messages {
for topic in message.topics() {
let message_data = message.encode();
self.gossipsub.publish(&topic.into(), message_data);
}
}
}
/// Forwards a message that is waiting in gossipsub's mcache. Messages are only propagated
/// once validated by the beacon chain.
pub fn propagate_message(&mut self, propagation_source: &PeerId, message_id: MessageId) {
self.gossipsub
.propagate_message(&message_id, propagation_source);
}
/* Eth2 RPC behaviour functions */
/// Sends an RPC Request/Response via the RPC protocol.
pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent<TSpec>) {
self.eth2_rpc.send_rpc(peer_id, rpc_event);
}
/* Discovery / Peer management functions */
/// The current number of connected libp2p peers.
pub fn connected_peers(&self) -> usize {
self.network_globals.connected_peers()
}
/// Notify discovery that the peer has been banned.
pub fn peer_banned(&mut self, peer_id: PeerId) {
self.discovery.peer_banned(peer_id);
}
/// Notify discovery that the peer has been unbanned.
pub fn peer_unbanned(&mut self, peer_id: &PeerId) {
self.discovery.peer_unbanned(peer_id);
}
/// Returns an iterator over all enr entries in the DHT.
pub fn enr_entries(&mut self) -> impl Iterator<Item = &Enr> {
self.discovery.enr_entries()
}
/// Add an ENR to the routing table of the discovery mechanism.
pub fn add_enr(&mut self, enr: Enr) {
self.discovery.add_enr(enr);
}
}
/// The types of events than can be obtained from polling the behaviour. /// The types of events than can be obtained from polling the behaviour.
pub enum BehaviourEvent<TSpec: EthSpec> { pub enum BehaviourEvent<TSpec: EthSpec> {
/// A received RPC event and the peer that it was received from. /// A received RPC event and the peer that it was received from.

View File

@ -7,6 +7,7 @@ use serde_derive::{Deserialize, Serialize};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::path::PathBuf; use std::path::PathBuf;
use std::time::Duration; use std::time::Duration;
use types::EnrForkId;
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(default)] #[serde(default)]
@ -63,6 +64,9 @@ pub struct Config {
/// List of extra topics to initially subscribe to as strings. /// List of extra topics to initially subscribe to as strings.
pub topics: Vec<GossipTopic>, pub topics: Vec<GossipTopic>,
/// The initial ENR fork id.
pub enr_fork_id: EnrForkId,
/// Introduces randomization in network propagation of messages. This should only be set for /// Introduces randomization in network propagation of messages. This should only be set for
/// testing purposes and will likely be removed in future versions. /// testing purposes and will likely be removed in future versions.
// TODO: Remove this functionality for mainnet // TODO: Remove this functionality for mainnet
@ -132,6 +136,7 @@ impl Default for Config {
libp2p_nodes: vec![], libp2p_nodes: vec![],
client_version: version::version(), client_version: version::version(),
topics, topics,
enr_fork_id: EnrForkId::default(),
propagation_percentage: None, propagation_percentage: None,
} }
} }

View File

@ -0,0 +1,121 @@
use super::ENR_FILENAME;
use crate::Enr;
use crate::NetworkConfig;
use libp2p::core::identity::Keypair;
use libp2p::discv5::enr::{CombinedKey, EnrBuilder};
use slog::{debug, warn};
use ssz::Encode;
use std::convert::TryInto;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;
use std::str::FromStr;
/// Loads an ENR from file if it exists and matches the current NodeId and sequence number. If none
/// exists, generates a new one.
///
/// If an ENR exists, with the same NodeId, this function checks to see if the loaded ENR from
/// disk is suitable to use, otherwise we increment our newly generated ENR's sequence number.
pub fn build_or_load_enr(
local_key: Keypair,
config: &NetworkConfig,
log: &slog::Logger,
) -> Result<Enr, String> {
// Build the local ENR.
// Note: Discovery should update the ENR record's IP to the external IP as seen by the
// majority of our peers, if the CLI doesn't expressly forbid it.
let enr_key: CombinedKey = local_key
.try_into()
.map_err(|_| "Invalid key type for ENR records")?;
let mut local_enr = build_enr(&enr_key, config)?;
let enr_f = config.network_dir.join(ENR_FILENAME);
if let Ok(mut enr_file) = File::open(enr_f.clone()) {
let mut enr_string = String::new();
match enr_file.read_to_string(&mut enr_string) {
Err(_) => debug!(log, "Could not read ENR from file"),
Ok(_) => {
match Enr::from_str(&enr_string) {
Ok(disk_enr) => {
// if the same node id, then we may need to update our sequence number
if local_enr.node_id() == disk_enr.node_id() {
if compare_enr(&local_enr, &disk_enr) {
debug!(log, "ENR loaded from disk"; "file" => format!("{:?}", enr_f));
// the stored ENR has the same configuration, use it
return Ok(disk_enr);
}
// same node id, different configuration - update the sequence number
let new_seq_no = disk_enr.seq().checked_add(1).ok_or_else(|| "ENR sequence number on file is too large. Remove it to generate a new NodeId")?;
local_enr.set_seq(new_seq_no, &enr_key).map_err(|e| {
format!("Could not update ENR sequence number: {:?}", e)
})?;
debug!(log, "ENR sequence number increased"; "seq" => new_seq_no);
}
}
Err(e) => {
warn!(log, "ENR from file could not be decoded"; "error" => format!("{:?}", e));
}
}
}
}
}
save_enr_to_disk(&config.network_dir, &local_enr, log);
Ok(local_enr)
}
/// Builds a lighthouse ENR given a `NetworkConfig`.
fn build_enr(enr_key: &CombinedKey, config: &NetworkConfig) -> Result<Enr, String> {
let mut builder = EnrBuilder::new("v4");
if let Some(enr_address) = config.enr_address {
builder.ip(enr_address);
}
if let Some(udp_port) = config.enr_udp_port {
builder.udp(udp_port);
}
// we always give it our listening tcp port
// TODO: Add uPnP support to map udp and tcp ports
let tcp_port = config.enr_tcp_port.unwrap_or_else(|| config.libp2p_port);
builder.tcp(tcp_port);
// set the `eth2` field on our ENR
builder.add_value("eth2".into(), config.enr_fork_id.as_ssz_bytes());
builder
.tcp(config.libp2p_port)
.build(enr_key)
.map_err(|e| format!("Could not build Local ENR: {:?}", e))
}
/// Defines the conditions under which we use the locally built ENR or the one stored on disk.
/// If this function returns true, we use the `disk_enr`.
fn compare_enr(local_enr: &Enr, disk_enr: &Enr) -> bool {
// take preference over disk_enr address if one is not specified
(local_enr.ip().is_none() || local_enr.ip() == disk_enr.ip())
// tcp ports must match
&& local_enr.tcp() == disk_enr.tcp()
&& local_enr.get("eth2") == disk_enr.get("eth2")
// take preference over disk udp port if one is not specified
&& (local_enr.udp().is_none() || local_enr.udp() == disk_enr.udp())
}
/// 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);
match File::create(dir.join(Path::new(ENR_FILENAME)))
.and_then(|mut f| f.write_all(&enr.to_base64().as_bytes()))
{
Ok(_) => {
debug!(log, "ENR written to disk");
}
Err(e) => {
warn!(
log,
"Could not write ENR to file"; "file" => format!("{:?}{:?}",dir, ENR_FILENAME), "error" => format!("{}", e)
);
}
}
}

View File

@ -1,29 +1,25 @@
///! This manages the discovery and management of peers.
mod enr_helpers;
use crate::metrics; use crate::metrics;
use crate::Enr; use crate::Enr;
use crate::{error, NetworkConfig, NetworkGlobals, PeerInfo}; use crate::{error, NetworkConfig, NetworkGlobals, PeerInfo};
/// This manages the discovery and management of peers.
///
/// Currently using discv5 for peer discovery.
///
use futures::prelude::*; use futures::prelude::*;
use libp2p::core::{identity::Keypair, ConnectedPoint, Multiaddr, PeerId}; use libp2p::core::{identity::Keypair, ConnectedPoint, Multiaddr, PeerId};
use libp2p::discv5::enr::{CombinedKey, EnrBuilder, NodeId}; use libp2p::discv5::enr::NodeId;
use libp2p::discv5::{Discv5, Discv5Event}; use libp2p::discv5::{Discv5, Discv5Event};
use libp2p::multiaddr::Protocol; use libp2p::multiaddr::Protocol;
use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters, ProtocolsHandler}; use libp2p::swarm::{NetworkBehaviour, NetworkBehaviourAction, PollParameters, ProtocolsHandler};
use slog::{debug, info, warn}; use slog::{debug, info, warn};
use ssz::Encode;
use std::collections::HashSet; use std::collections::HashSet;
use std::convert::TryInto;
use std::fs::File;
use std::io::prelude::*;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::path::Path; use std::path::Path;
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tokio::io::{AsyncRead, AsyncWrite}; use tokio::io::{AsyncRead, AsyncWrite};
use tokio::timer::Delay; use tokio::timer::Delay;
use types::EthSpec; use types::{EnrForkId, EthSpec};
/// Maximum seconds before searching for extra peers. /// Maximum seconds before searching for extra peers.
const MAX_TIME_BETWEEN_PEER_SEARCHES: u64 = 120; const MAX_TIME_BETWEEN_PEER_SEARCHES: u64 = 120;
@ -76,7 +72,7 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
let log = log.clone(); let log = log.clone();
// checks if current ENR matches that found on disk // checks if current ENR matches that found on disk
let local_enr = load_enr(local_key.clone(), config, &log)?; let local_enr = enr_helpers::build_or_load_enr(local_key.clone(), config, &log)?;
*network_globals.local_enr.write() = Some(local_enr.clone()); *network_globals.local_enr.write() = Some(local_enr.clone());
@ -105,7 +101,13 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
"node_id" => format!("{}", bootnode_enr.node_id()), "node_id" => format!("{}", bootnode_enr.node_id()),
"peer_id" => format!("{}", bootnode_enr.peer_id()) "peer_id" => format!("{}", bootnode_enr.peer_id())
); );
discovery.add_enr(bootnode_enr); let _ = discovery.add_enr(bootnode_enr).map_err(|e| {
warn!(
log,
"Could not add peer to the local routing table";
"error" => format!("{}", e)
)
});
} }
Ok(Self { Ok(Self {
@ -135,7 +137,13 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
/// Add an ENR to the routing table of the discovery mechanism. /// Add an ENR to the routing table of the discovery mechanism.
pub fn add_enr(&mut self, enr: Enr) { pub fn add_enr(&mut self, enr: Enr) {
self.discovery.add_enr(enr); let _ = self.discovery.add_enr(enr).map_err(|e| {
warn!(
self.log,
"Could not add peer to the local routing table";
"error" => format!("{}", e)
)
});
} }
/// The peer has been banned. Add this peer to the banned list to prevent any future /// The peer has been banned. Add this peer to the banned list to prevent any future
@ -154,6 +162,34 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
self.discovery.enr_entries() self.discovery.enr_entries()
} }
/// Updates the `eth2` field of our local ENR.
pub fn update_eth2_enr(&mut self, enr_fork_id: EnrForkId) {
// to avoid having a reference to the spec constant, for the logging we assume
// FAR_FUTURE_EPOCH is u64::max_value()
let next_fork_epoch_log = if enr_fork_id.next_fork_epoch == u64::max_value() {
String::from("No other fork")
} else {
format!("{:?}", enr_fork_id.next_fork_epoch)
};
info!(self.log, "Updating the ENR fork version";
"fork_digest" => format!("{:?}", enr_fork_id.fork_digest),
"next_fork_version" => format!("{:?}", enr_fork_id.next_fork_version),
"next_fork_epoch" => next_fork_epoch_log,
);
let _ = self
.discovery
.enr_insert("eth2".into(), enr_fork_id.as_ssz_bytes())
.map_err(|e| {
warn!(
self.log,
"Could not update eth2 ENR field";
"error" => format!("{:?}", e)
)
});
}
/// Search for new peers using the underlying discovery mechanism. /// Search for new peers using the underlying discovery mechanism.
fn find_peers(&mut self) { fn find_peers(&mut self) {
// pick a random NodeId // pick a random NodeId
@ -268,13 +304,14 @@ where
let mut address = Multiaddr::from(socket.ip()); let mut address = Multiaddr::from(socket.ip());
address.push(Protocol::Tcp(self.tcp_port)); address.push(Protocol::Tcp(self.tcp_port));
let enr = self.discovery.local_enr(); let enr = self.discovery.local_enr();
save_enr_to_disc(Path::new(&self.enr_dir), enr, &self.log); enr_helpers::save_enr_to_disk(Path::new(&self.enr_dir), enr, &self.log);
return Async::Ready(NetworkBehaviourAction::ReportObservedAddr { return Async::Ready(NetworkBehaviourAction::ReportObservedAddr {
address, address,
}); });
} }
Discv5Event::FindNodeResult { closer_peers, .. } => { Discv5Event::FindNodeResult { closer_peers, .. } => {
// TODO: Modify once ENR predicate search is available
debug!(self.log, "Discovery query completed"; "peers_found" => closer_peers.len()); debug!(self.log, "Discovery query completed"; "peers_found" => closer_peers.len());
// update the time to the next query // update the time to the next query
if self.past_discovery_delay < MAX_TIME_BETWEEN_PEER_SEARCHES { if self.past_discovery_delay < MAX_TIME_BETWEEN_PEER_SEARCHES {
@ -320,94 +357,3 @@ where
Async::NotReady Async::NotReady
} }
} }
/// Loads an ENR from file if it exists and matches the current NodeId and sequence number. If none
/// exists, generates a new one.
///
/// If an ENR exists, with the same NodeId and IP address, we use the disk-generated one as its
/// ENR sequence will be equal or higher than a newly generated one.
fn load_enr(local_key: Keypair, config: &NetworkConfig, log: &slog::Logger) -> Result<Enr, String> {
// Build the local ENR.
// Note: Discovery should update the ENR record's IP to the external IP as seen by the
// majority of our peers, if the CLI doesn't expressly forbid it.
let enr_key: CombinedKey = local_key
.try_into()
.map_err(|_| "Invalid key type for ENR records")?;
let mut local_enr = {
let mut builder = EnrBuilder::new("v4");
if let Some(enr_address) = config.enr_address {
builder.ip(enr_address);
}
if let Some(udp_port) = config.enr_udp_port {
builder.udp(udp_port);
}
// we always give it our listening tcp port
// TODO: Add uPnP support to map udp and tcp ports
let tcp_port = config.enr_tcp_port.unwrap_or_else(|| config.libp2p_port);
builder.tcp(tcp_port);
builder
.tcp(config.libp2p_port)
.build(&enr_key)
.map_err(|e| format!("Could not build Local ENR: {:?}", e))?
};
let enr_f = config.network_dir.join(ENR_FILENAME);
if let Ok(mut enr_file) = File::open(enr_f.clone()) {
let mut enr_string = String::new();
match enr_file.read_to_string(&mut enr_string) {
Err(_) => debug!(log, "Could not read ENR from file"),
Ok(_) => {
match Enr::from_str(&enr_string) {
Ok(enr) => {
let tcp_port = config.enr_tcp_port.unwrap_or_else(|| config.libp2p_port);
if enr.node_id() == local_enr.node_id() {
if (config.enr_address.is_none()
|| enr.ip().map(Into::into) == config.enr_address)
&& enr.tcp() == Some(tcp_port)
&& (config.enr_udp_port.is_none()
|| enr.udp() == config.enr_udp_port)
{
debug!(log, "ENR loaded from file"; "file" => format!("{:?}", enr_f));
// the stored ENR has the same configuration, use it
return Ok(enr);
}
// same node id, different configuration - update the sequence number
let new_seq_no = enr.seq().checked_add(1).ok_or_else(|| "ENR sequence number on file is too large. Remove it to generate a new NodeId")?;
local_enr.set_seq(new_seq_no, &enr_key).map_err(|e| {
format!("Could not update ENR sequence number: {:?}", e)
})?;
debug!(log, "ENR sequence number increased"; "seq" => new_seq_no);
}
}
Err(e) => {
warn!(log, "ENR from file could not be decoded"; "error" => format!("{:?}", e));
}
}
}
}
}
save_enr_to_disc(&config.network_dir, &local_enr, log);
Ok(local_enr)
}
fn save_enr_to_disc(dir: &Path, enr: &Enr, log: &slog::Logger) {
let _ = std::fs::create_dir_all(dir);
match File::create(dir.join(Path::new(ENR_FILENAME)))
.and_then(|mut f| f.write_all(&enr.to_base64().as_bytes()))
{
Ok(_) => {
debug!(log, "ENR written to disk");
}
Err(e) => {
warn!(
log,
"Could not write ENR to file"; "file" => format!("{:?}{:?}",dir, ENR_FILENAME), "error" => format!("{}", e)
);
}
}
}

View File

@ -117,7 +117,7 @@ fn build_secio_transport(local_private_key: Keypair) -> Boxed<(PeerId, StreamMux
fn test_secio_noise_fallback() { fn test_secio_noise_fallback() {
// set up the logging. The level and enabled logging or not // set up the logging. The level and enabled logging or not
let log_level = Level::Trace; let log_level = Level::Trace;
let enable_logging = true; let enable_logging = false;
let log = common::build_log(log_level, enable_logging); let log = common::build_log(log_level, enable_logging);

View File

@ -1,5 +1,5 @@
///! Maintains a hard-coded list of known forks and their slots at which they were activated. ///! Maintains a hard-coded list of known forks and their slots at which they were activated.
use types::{Epoch, EthSpec, Slot, FAR_FUTURE_EPOCH}; use types::{ChainSpec, Epoch, EthSpec, Slot};
mod forks; mod forks;
@ -7,7 +7,7 @@ mod forks;
/// number. /// number.
/// ///
/// The disabled_forks parameter select which forks are disabled by their name. /// The disabled_forks parameter select which forks are disabled by their name.
pub fn current_fork_version(slot: Slot, disabled_forks: Vec<String>) -> [u8; 4] { pub fn current_fork_version(slot: Slot, disabled_forks: &[String]) -> [u8; 4] {
let mut version = [0, 0, 0, 0]; let mut version = [0, 0, 0, 0];
for (fork_name, fork_slot_no, fork_version) in forks::KNOWN_FORKS.iter() { for (fork_name, fork_slot_no, fork_version) in forks::KNOWN_FORKS.iter() {
if *fork_slot_no <= slot.as_u64() { if *fork_slot_no <= slot.as_u64() {
@ -25,7 +25,7 @@ pub fn current_fork_version(slot: Slot, disabled_forks: Vec<String>) -> [u8; 4]
version version
} }
pub fn next_fork_version(slot: Slot, disabled_forks: Vec<String>) -> [u8; 4] { pub fn next_fork_version(slot: Slot, disabled_forks: &[String]) -> [u8; 4] {
let mut version = None; let mut version = None;
for (fork_name, fork_slot_no, fork_version) in forks::KNOWN_FORKS.iter() { for (fork_name, fork_slot_no, fork_version) in forks::KNOWN_FORKS.iter() {
if *fork_slot_no > slot.as_u64() { if *fork_slot_no > slot.as_u64() {
@ -48,7 +48,11 @@ pub fn next_fork_version(slot: Slot, disabled_forks: Vec<String>) -> [u8; 4] {
} }
} }
pub fn next_fork_epoch<T: EthSpec>(slot: Slot, disabled_forks: Vec<String>) -> Epoch { pub fn next_fork_epoch<T: EthSpec>(
spec: &ChainSpec,
slot: Slot,
disabled_forks: &[String],
) -> Epoch {
let mut next_fork_slot = None; let mut next_fork_slot = None;
for (fork_name, fork_slot_no, _fork_version) in forks::KNOWN_FORKS.iter() { for (fork_name, fork_slot_no, _fork_version) in forks::KNOWN_FORKS.iter() {
if *fork_slot_no > slot.as_u64() { if *fork_slot_no > slot.as_u64() {
@ -66,6 +70,6 @@ pub fn next_fork_epoch<T: EthSpec>(slot: Slot, disabled_forks: Vec<String>) -> E
if let Some(fork_slot) = next_fork_slot { if let Some(fork_slot) = next_fork_slot {
fork_slot.epoch(T::slots_per_epoch()) fork_slot.epoch(T::slots_per_epoch())
} else { } else {
FAR_FUTURE_EPOCH Epoch::from(spec.far_future_epoch)
} }
} }

View File

@ -27,6 +27,8 @@ const BAN_PEER_TIMEOUT: u64 = 30;
/// Service that handles communication between internal services and the `eth2_libp2p` network service. /// Service that handles communication between internal services and the `eth2_libp2p` network service.
pub struct NetworkService<T: BeaconChainTypes> { pub struct NetworkService<T: BeaconChainTypes> {
/// A reference to the underlying beacon chain.
beacon_chain: Arc<BeaconChain<T>>,
/// The underlying libp2p service that drives all the network interactions. /// The underlying libp2p service that drives all the network interactions.
libp2p: LibP2PService<T::EthSpec>, libp2p: LibP2PService<T::EthSpec>,
/// An attestation and subnet manager service. /// An attestation and subnet manager service.
@ -42,6 +44,8 @@ pub struct NetworkService<T: BeaconChainTypes> {
network_globals: Arc<NetworkGlobals<T::EthSpec>>, network_globals: Arc<NetworkGlobals<T::EthSpec>>,
/// An initial delay to update variables after the libp2p service has started. /// An initial delay to update variables after the libp2p service has started.
initial_delay: Delay, initial_delay: Delay,
/// A delay that expires when a new fork takes place.
next_fork_update: Option<Delay>,
/// The logger for the network service. /// The logger for the network service.
log: slog::Logger, log: slog::Logger,
/// A probability of propagation. /// A probability of propagation.
@ -51,7 +55,7 @@ pub struct NetworkService<T: BeaconChainTypes> {
impl<T: BeaconChainTypes> NetworkService<T> { impl<T: BeaconChainTypes> NetworkService<T> {
pub fn start( pub fn start(
beacon_chain: Arc<BeaconChain<T>>, beacon_chain: Arc<BeaconChain<T>>,
config: &NetworkConfig, config: &mut NetworkConfig,
executor: &TaskExecutor, executor: &TaskExecutor,
network_log: slog::Logger, network_log: slog::Logger,
) -> error::Result<( ) -> error::Result<(
@ -72,6 +76,17 @@ impl<T: BeaconChainTypes> NetworkService<T> {
)?; )?;
let propagation_percentage = config.propagation_percentage; let propagation_percentage = config.propagation_percentage;
// set the local enr_fork_id
config.enr_fork_id = beacon_chain
.enr_fork_id()
.map_err(|e| format!("Could not get the current ENR fork version: {:?}", e))?;
// keep track of when our fork_id needs to be updated
let next_fork_update = beacon_chain
.duration_to_next_fork()
.map_err(|e| format!("Could not get the next fork update duration: {:?}", e))?;
// launch libp2p service // launch libp2p service
let (network_globals, mut libp2p) = LibP2PService::new(config, network_log.clone())?; let (network_globals, mut libp2p) = LibP2PService::new(config, network_log.clone())?;
@ -85,10 +100,11 @@ impl<T: BeaconChainTypes> NetworkService<T> {
// create the attestation service // create the attestation service
let attestation_service = let attestation_service =
AttestationService::new(beacon_chain, network_globals.clone(), &network_log); AttestationService::new(beacon_chain.clone(), network_globals.clone(), &network_log);
// create the network service and spawn the task // create the network service and spawn the task
let network_service = NetworkService { let network_service = NetworkService {
beacon_chain,
libp2p, libp2p,
attestation_service, attestation_service,
network_recv, network_recv,
@ -96,6 +112,7 @@ impl<T: BeaconChainTypes> NetworkService<T> {
store, store,
network_globals: network_globals.clone(), network_globals: network_globals.clone(),
initial_delay, initial_delay,
next_fork_update,
log: network_log, log: network_log,
propagation_percentage, propagation_percentage,
}; };
@ -118,6 +135,7 @@ fn spawn_service<T: BeaconChainTypes>(
let log = &service.log; let log = &service.log;
// handles any logic which requires an initial delay
if !service.initial_delay.is_elapsed() { if !service.initial_delay.is_elapsed() {
if let Ok(Async::Ready(_)) = service.initial_delay.poll() { if let Ok(Async::Ready(_)) = service.initial_delay.poll() {
let multi_addrs = Swarm::listeners(&service.libp2p.swarm).cloned().collect(); let multi_addrs = Swarm::listeners(&service.libp2p.swarm).cloned().collect();
@ -306,6 +324,18 @@ fn spawn_service<T: BeaconChainTypes>(
); );
} }
// if we have just forked, update inform the libp2p layer
if let Some(mut update_fork_delay) = service.next_fork_update.take() {
if !update_fork_delay.is_elapsed() {
if let Ok(Async::Ready(_)) = update_fork_delay.poll() {
if let Ok(enr_fork_id) = service.beacon_chain.enr_fork_id() {
service.libp2p.swarm.update_fork_version(enr_fork_id);
}
service.next_fork_update = service.beacon_chain.duration_to_next_fork().unwrap_or_else(|_| None);
}
}
}
Ok(Async::NotReady) Ok(Async::NotReady)
}) })

View File

@ -150,6 +150,14 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
automatically.") automatically.")
.takes_value(true), .takes_value(true),
) )
/* Client/chain related arguments */
.arg(
Arg::with_name("disabled-forks")
.long("disabled-forks")
.value_name("STRING")
.help("A comma separated list of forks that will be disabled.")
.takes_value(true),
)
/* REST API related arguments */ /* REST API related arguments */
.arg( .arg(
Arg::with_name("http") Arg::with_name("http")

View File

@ -187,6 +187,19 @@ pub fn get_configs<E: EthSpec>(
client_config.network.secret_key_hex = Some(p2p_priv_key.to_string()); client_config.network.secret_key_hex = Some(p2p_priv_key.to_string());
} }
/*
* Chain specification
*/
if let Some(disabled_forks_str) = cli_args.value_of("disabled-forks") {
client_config.disabled_forks = disabled_forks_str
.split(',')
.map(|fork_name| {
fork_name
.parse()
.map_err(|_| format!("Invalid fork name: {}", fork_name))
})
.collect::<Result<Vec<String>>>()?;
}
/* /*
* Http server * Http server
*/ */

View File

@ -72,7 +72,7 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
/// Client behaviour is defined by the given `client_config`. /// Client behaviour is defined by the given `client_config`.
pub fn new( pub fn new(
context: RuntimeContext<E>, context: RuntimeContext<E>,
client_config: ClientConfig, mut client_config: ClientConfig,
) -> impl Future<Item = Self, Error = String> { ) -> impl Future<Item = Self, Error = String> {
let http_eth2_config = context.eth2_config().clone(); let http_eth2_config = context.eth2_config().clone();
let spec = context.eth2_config().spec.clone(); let spec = context.eth2_config().spec.clone();
@ -124,7 +124,7 @@ impl<E: EthSpec> ProductionBeaconNode<E> {
.system_time_slot_clock()? .system_time_slot_clock()?
.websocket_event_handler(client_config.websocket_server.clone())? .websocket_event_handler(client_config.websocket_server.clone())?
.build_beacon_chain()? .build_beacon_chain()?
.network(&client_config.network)? .network(&mut client_config.network)?
.notifier()?; .notifier()?;
let builder = if client_config.rest_api.enabled { let builder = if client_config.rest_api.enabled {