Connects the attestation service to network components (#961)

* Sends attestations to the attestation service for processing

* Adds 'attnets' field to local ENR

* Adds ENR bitfield modification logic

* Link attestation service to discovery

- Updates discv5
- Links discover events to discovery
- Support for ENRBitfield

* Adds discovery config params, correct warnings

* Rust fmt fixes

* Correct tests
This commit is contained in:
Age Manning 2020-03-25 22:18:06 +11:00 committed by GitHub
parent fbcf0f8e2e
commit 6ca4f4709b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 381 additions and 149 deletions

55
Cargo.lock generated
View File

@ -1090,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"base64 0.12.0", "base64 0.12.0",
"bs58 0.3.0", "bs58 0.3.0",
@ -1210,6 +1210,7 @@ dependencies = [
"error-chain", "error-chain",
"eth2_ssz", "eth2_ssz",
"eth2_ssz_derive", "eth2_ssz_derive",
"eth2_ssz_types",
"fnv", "fnv",
"futures", "futures",
"hex 0.3.2", "hex 0.3.2",
@ -2058,7 +2059,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2097,7 +2098,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"asn1_der", "asn1_der",
"bs58 0.3.0", "bs58 0.3.0",
@ -2132,7 +2133,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"quote 1.0.3", "quote 1.0.3",
"syn 1.0.16", "syn 1.0.16",
@ -2141,7 +2142,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"flate2", "flate2",
"futures", "futures",
@ -2152,7 +2153,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"arrayvec 0.4.12", "arrayvec 0.4.12",
"bigint", "bigint",
@ -2183,7 +2184,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"futures", "futures",
"libp2p-core", "libp2p-core",
@ -2194,7 +2195,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bs58 0.3.0", "bs58 0.3.0",
"bytes", "bytes",
@ -2212,7 +2213,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"base64 0.10.1", "base64 0.10.1",
"bs58 0.2.5", "bs58 0.2.5",
@ -2237,7 +2238,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2256,7 +2257,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"arrayvec 0.5.1", "arrayvec 0.5.1",
"bytes", "bytes",
@ -2283,7 +2284,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"data-encoding", "data-encoding",
"dns-parser", "dns-parser",
@ -2305,7 +2306,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"fnv", "fnv",
@ -2321,7 +2322,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"curve25519-dalek 1.2.3", "curve25519-dalek 1.2.3",
@ -2341,7 +2342,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2358,7 +2359,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2373,7 +2374,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"aes-ctr", "aes-ctr",
"bytes", "bytes",
@ -2402,7 +2403,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"futures", "futures",
"libp2p-core", "libp2p-core",
@ -2415,7 +2416,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2431,7 +2432,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"futures", "futures",
"libp2p-core", "libp2p-core",
@ -2442,7 +2443,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"futures", "futures",
"js-sys", "js-sys",
@ -2456,7 +2457,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -2474,7 +2475,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"futures", "futures",
"libp2p-core", "libp2p-core",
@ -2774,7 +2775,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",
@ -3007,7 +3008,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"arrayref", "arrayref",
"bs58 0.3.0", "bs58 0.3.0",
@ -3024,7 +3025,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"blake2", "blake2",
"bytes", "bytes",
@ -3800,7 +3801,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=8c272a9a4d115d9a1d33791479527cdcba781829#8c272a9a4d115d9a1d33791479527cdcba781829" source = "git+https://github.com/SigP/rust-libp2p?rev=44d7a9c9cd7be74109817bcabe74b991d5bd0fee#44d7a9c9cd7be74109817bcabe74b991d5bd0fee"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures", "futures",

View File

@ -252,7 +252,7 @@ where
} }
/// Immediately starts the networking stack. /// Immediately starts the networking stack.
pub fn network(mut self, config: &mut NetworkConfig) -> Result<Self, String> { pub fn network(mut self, config: &NetworkConfig) -> Result<Self, String> {
let beacon_chain = self let beacon_chain = self
.beacon_chain .beacon_chain
.clone() .clone()

View File

@ -8,8 +8,9 @@ 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 = "8c272a9a4d115d9a1d33791479527cdcba781829" } libp2p = { git = "https://github.com/SigP/rust-libp2p", rev = "44d7a9c9cd7be74109817bcabe74b991d5bd0fee" }
types = { path = "../../eth2/types" } types = { path = "../../eth2/types" }
eth2_ssz_types = { path = "../../eth2/utils/ssz_types" }
serde = "1.0.102" serde = "1.0.102"
serde_derive = "1.0.102" serde_derive = "1.0.102"
eth2_ssz = "0.1.2" eth2_ssz = "0.1.2"

View File

@ -1,5 +1,6 @@
use crate::discovery::Discovery; use crate::discovery::Discovery;
use crate::rpc::{RPCEvent, RPCMessage, RPC}; use crate::rpc::{RPCEvent, RPCMessage, RPC};
use crate::types::GossipEncoding;
use crate::Enr; use crate::Enr;
use crate::{error, GossipTopic, NetworkConfig, NetworkGlobals, PubsubMessage, TopicHash}; use crate::{error, GossipTopic, NetworkConfig, NetworkGlobals, PubsubMessage, TopicHash};
use futures::prelude::*; use futures::prelude::*;
@ -13,9 +14,9 @@ use libp2p::{
NetworkBehaviour, PeerId, NetworkBehaviour, PeerId,
}; };
use lru::LruCache; use lru::LruCache;
use slog::{debug, o, warn}; use slog::{crit, debug, o, warn};
use std::sync::Arc; use std::sync::Arc;
use types::{EnrForkId, EthSpec}; use types::{EnrForkId, EthSpec, SubnetId};
const MAX_IDENTIFY_ADDRESSES: usize = 20; const MAX_IDENTIFY_ADDRESSES: usize = 20;
@ -55,6 +56,7 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
local_key: &Keypair, local_key: &Keypair,
net_conf: &NetworkConfig, net_conf: &NetworkConfig,
network_globals: Arc<NetworkGlobals<TSpec>>, network_globals: Arc<NetworkGlobals<TSpec>>,
enr_fork_id: EnrForkId,
log: &slog::Logger, log: &slog::Logger,
) -> error::Result<Self> { ) -> error::Result<Self> {
let local_peer_id = local_key.public().into_peer_id(); let local_peer_id = local_key.public().into_peer_id();
@ -69,7 +71,13 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
Ok(Behaviour { Ok(Behaviour {
eth2_rpc: RPC::new(log.clone()), eth2_rpc: RPC::new(log.clone()),
gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()), gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()),
discovery: Discovery::new(local_key, net_conf, network_globals.clone(), log)?, discovery: Discovery::new(
local_key,
net_conf,
enr_fork_id,
network_globals.clone(),
log,
)?,
identify, identify,
events: Vec::new(), events: Vec::new(),
seen_gossip_messages: LruCache::new(100_000), seen_gossip_messages: LruCache::new(100_000),
@ -107,6 +115,12 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
self.gossipsub.subscribe(topic.into()) self.gossipsub.subscribe(topic.into())
} }
/// Subscribes to a specific subnet id;
pub fn subscribe_to_subnet(&mut self, subnet_id: SubnetId) {
let topic = GossipTopic::new(subnet_id.into(), GossipEncoding::SSZ);
self.subscribe(topic);
}
/// Unsubscribe from a gossipsub topic. /// Unsubscribe from a gossipsub topic.
pub fn unsubscribe(&mut self, topic: GossipTopic) -> bool { pub fn unsubscribe(&mut self, topic: GossipTopic) -> bool {
let pos = self let pos = self
@ -124,6 +138,12 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
self.gossipsub.unsubscribe(topic.into()) self.gossipsub.unsubscribe(topic.into())
} }
/// Un-Subscribes from a specific subnet id;
pub fn unsubscribe_from_subnet(&mut self, subnet_id: SubnetId) {
let topic = GossipTopic::new(subnet_id.into(), GossipEncoding::SSZ);
self.unsubscribe(topic);
}
/// Publishes a list of messages on the pubsub (gossipsub) behaviour, choosing the encoding. /// Publishes a list of messages on the pubsub (gossipsub) behaviour, choosing the encoding.
pub fn publish(&mut self, messages: Vec<PubsubMessage<TSpec>>) { pub fn publish(&mut self, messages: Vec<PubsubMessage<TSpec>>) {
for message in messages { for message in messages {
@ -170,6 +190,20 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
self.discovery.add_enr(enr); self.discovery.add_enr(enr);
} }
/// Updates a subnet value to the ENR bitfield.
///
/// The `value` is `true` if a subnet is being added and false otherwise.
pub fn update_enr_subnet(&mut self, subnet_id: SubnetId, value: bool) {
if let Err(e) = self.discovery.update_enr_bitfield(subnet_id, value) {
crit!(self.log, "Could not update ENR bitfield"; "error" => e);
}
}
/// A request to search for peers connected to a long-lived subnet.
pub fn peers_request(&mut self, subnet_id: SubnetId) {
self.discovery.peers_request(subnet_id);
}
/// Updates the local ENR's "eth2" field with the latest EnrForkId. /// Updates the local ENR's "eth2" field with the latest EnrForkId.
pub fn update_fork_version(&mut self, enr_fork_id: EnrForkId) { pub fn update_fork_version(&mut self, enr_fork_id: EnrForkId) {
self.discovery.update_eth2_enr(enr_fork_id); self.discovery.update_eth2_enr(enr_fork_id);

View File

@ -7,7 +7,6 @@ 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)]
@ -64,9 +63,6 @@ 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
@ -112,10 +108,11 @@ impl Default for Config {
// discv5 configuration // discv5 configuration
let discv5_config = Discv5ConfigBuilder::new() let discv5_config = Discv5ConfigBuilder::new()
.request_timeout(Duration::from_secs(4)) .request_timeout(Duration::from_secs(4))
.request_retries(1) .request_retries(2)
.enr_update(true) // update IP based on PONG responses .enr_update(true) // update IP based on PONG responses
.enr_peer_update_min(2) // prevents NAT's should be raised for mainnet .enr_peer_update_min(2) // prevents NAT's should be raised for mainnet
.query_parallelism(5) .query_parallelism(5)
.query_timeout(Duration::from_secs(2))
.ip_limit(false) // limits /24 IP's in buckets. Enable for mainnet .ip_limit(false) // limits /24 IP's in buckets. Enable for mainnet
.ping_interval(Duration::from_secs(300)) .ping_interval(Duration::from_secs(300))
.build(); .build();
@ -136,7 +133,6 @@ 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

@ -5,20 +5,28 @@ use libp2p::core::identity::Keypair;
use libp2p::discv5::enr::{CombinedKey, EnrBuilder}; use libp2p::discv5::enr::{CombinedKey, EnrBuilder};
use slog::{debug, warn}; use slog::{debug, warn};
use ssz::Encode; use ssz::Encode;
use ssz_types::BitVector;
use std::convert::TryInto; use std::convert::TryInto;
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::path::Path; use std::path::Path;
use std::str::FromStr; use std::str::FromStr;
use types::{EnrForkId, EthSpec};
/// The ENR field specifying the fork id.
pub const ETH2_ENR_KEY: &'static str = "eth2";
/// The ENR field specifying the subnet bitfield.
pub const BITFIELD_ENR_KEY: &'static str = "attnets";
/// Loads an ENR from file if it exists and matches the current NodeId and sequence number. If none /// Loads an ENR from file if it exists and matches the current NodeId and sequence number. If none
/// exists, generates a new one. /// exists, generates a new one.
/// ///
/// If an ENR exists, with the same NodeId, this function checks to see if the loaded ENR from /// 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. /// disk is suitable to use, otherwise we increment our newly generated ENR's sequence number.
pub fn build_or_load_enr( pub fn build_or_load_enr<T: EthSpec>(
local_key: Keypair, local_key: Keypair,
config: &NetworkConfig, config: &NetworkConfig,
enr_fork_id: EnrForkId,
log: &slog::Logger, log: &slog::Logger,
) -> Result<Enr, String> { ) -> Result<Enr, String> {
// Build the local ENR. // Build the local ENR.
@ -28,7 +36,7 @@ pub fn build_or_load_enr(
.try_into() .try_into()
.map_err(|_| "Invalid key type for ENR records")?; .map_err(|_| "Invalid key type for ENR records")?;
let mut local_enr = build_enr(&enr_key, config)?; let mut local_enr = build_enr::<T>(&enr_key, config, enr_fork_id)?;
let enr_f = config.network_dir.join(ENR_FILENAME); let enr_f = config.network_dir.join(ENR_FILENAME);
if let Ok(mut enr_file) = File::open(enr_f.clone()) { if let Ok(mut enr_file) = File::open(enr_f.clone()) {
@ -68,7 +76,11 @@ pub fn build_or_load_enr(
} }
/// Builds a lighthouse ENR given a `NetworkConfig`. /// Builds a lighthouse ENR given a `NetworkConfig`.
fn build_enr(enr_key: &CombinedKey, config: &NetworkConfig) -> Result<Enr, String> { fn build_enr<T: EthSpec>(
enr_key: &CombinedKey,
config: &NetworkConfig,
enr_fork_id: EnrForkId,
) -> Result<Enr, String> {
let mut builder = EnrBuilder::new("v4"); let mut builder = EnrBuilder::new("v4");
if let Some(enr_address) = config.enr_address { if let Some(enr_address) = config.enr_address {
builder.ip(enr_address); builder.ip(enr_address);
@ -82,7 +94,12 @@ fn build_enr(enr_key: &CombinedKey, config: &NetworkConfig) -> Result<Enr, Strin
builder.tcp(tcp_port); builder.tcp(tcp_port);
// set the `eth2` field on our ENR // set the `eth2` field on our ENR
builder.add_value("eth2".into(), config.enr_fork_id.as_ssz_bytes()); builder.add_value(ETH2_ENR_KEY.into(), enr_fork_id.as_ssz_bytes());
// set the "attnets" field on our ENR
let bitfield = BitVector::<T::SubnetBitfieldLength>::new();
builder.add_value(BITFIELD_ENR_KEY.into(), bitfield.as_ssz_bytes());
builder builder
.tcp(config.libp2p_port) .tcp(config.libp2p_port)
@ -97,9 +114,13 @@ fn compare_enr(local_enr: &Enr, disk_enr: &Enr) -> bool {
(local_enr.ip().is_none() || local_enr.ip() == disk_enr.ip()) (local_enr.ip().is_none() || local_enr.ip() == disk_enr.ip())
// tcp ports must match // tcp ports must match
&& local_enr.tcp() == disk_enr.tcp() && local_enr.tcp() == disk_enr.tcp()
&& local_enr.get("eth2") == disk_enr.get("eth2") // must match on the same fork
&& local_enr.get(ETH2_ENR_KEY) == disk_enr.get(ETH2_ENR_KEY)
// take preference over disk udp port if one is not specified // take preference over disk udp port if one is not specified
&& (local_enr.udp().is_none() || local_enr.udp() == disk_enr.udp()) && (local_enr.udp().is_none() || local_enr.udp() == disk_enr.udp())
// we need the BITFIELD_ENR_KEY key to match, otherwise we use a new ENR. This will likely only
// be true for non-validating nodes
&& local_enr.get(BITFIELD_ENR_KEY) == disk_enr.get(BITFIELD_ENR_KEY)
} }
/// Saves an ENR to disk /// Saves an ENR to disk

View File

@ -2,8 +2,10 @@
mod enr_helpers; mod enr_helpers;
use crate::metrics; use crate::metrics;
use crate::types::EnrBitfield;
use crate::Enr; use crate::Enr;
use crate::{error, NetworkConfig, NetworkGlobals, PeerInfo}; use crate::{error, NetworkConfig, NetworkGlobals, PeerInfo};
use enr_helpers::{BITFIELD_ENR_KEY, ETH2_ENR_KEY};
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::NodeId; use libp2p::discv5::enr::NodeId;
@ -11,7 +13,8 @@ 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 ssz::{Decode, Encode};
use ssz_types::BitVector;
use std::collections::HashSet; use std::collections::HashSet;
use std::net::SocketAddr; use std::net::SocketAddr;
use std::path::Path; use std::path::Path;
@ -19,7 +22,7 @@ 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::{EnrForkId, EthSpec}; use types::{EnrForkId, EthSpec, SubnetId};
/// 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;
@ -27,6 +30,8 @@ const MAX_TIME_BETWEEN_PEER_SEARCHES: u64 = 120;
const INITIAL_SEARCH_DELAY: u64 = 5; const INITIAL_SEARCH_DELAY: u64 = 5;
/// Local ENR storage filename. /// Local ENR storage filename.
const ENR_FILENAME: &str = "enr.dat"; const ENR_FILENAME: &str = "enr.dat";
/// Number of peers we'd like to have connected to a given long-lived subnet.
const TARGET_SUBNET_PEERS: u64 = 3;
/// Lighthouse discovery behaviour. This provides peer management and discovery using the Discv5 /// Lighthouse discovery behaviour. This provides peer management and discovery using the Discv5
/// libp2p protocol. /// libp2p protocol.
@ -66,13 +71,15 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
pub fn new( pub fn new(
local_key: &Keypair, local_key: &Keypair,
config: &NetworkConfig, config: &NetworkConfig,
enr_fork_id: EnrForkId,
network_globals: Arc<NetworkGlobals<TSpec>>, network_globals: Arc<NetworkGlobals<TSpec>>,
log: &slog::Logger, log: &slog::Logger,
) -> error::Result<Self> { ) -> error::Result<Self> {
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 = enr_helpers::build_or_load_enr(local_key.clone(), config, &log)?; let local_enr =
enr_helpers::build_or_load_enr::<TSpec>(local_key.clone(), config, enr_fork_id, &log)?;
*network_globals.local_enr.write() = Some(local_enr.clone()); *network_globals.local_enr.write() = Some(local_enr.clone());
@ -162,6 +169,51 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
self.discovery.enr_entries() self.discovery.enr_entries()
} }
/// Adds/Removes a subnet from the ENR Bitfield
pub fn update_enr_bitfield(&mut self, subnet_id: SubnetId, value: bool) -> Result<(), String> {
let id = *subnet_id as usize;
let local_enr = self.discovery.local_enr();
let bitfield_bytes = local_enr
.get(BITFIELD_ENR_KEY)
.ok_or_else(|| "ENR bitfield non-existent")?;
let mut current_bitfield =
BitVector::<TSpec::SubnetBitfieldLength>::from_ssz_bytes(bitfield_bytes)
.map_err(|_| "Could not decode local ENR SSZ bitfield")?;
if id >= current_bitfield.len() {
return Err(format!(
"Subnet id: {} is outside the ENR bitfield length: {}",
id,
current_bitfield.len()
));
}
if current_bitfield
.get(id)
.map_err(|_| String::from("Subnet ID out of bounds"))?
== value
{
return Err(format!(
"Subnet id: {} already in the local ENR already has value: {}",
id, value
));
}
// set the subnet bitfield in the ENR
current_bitfield
.set(id, value)
.map_err(|_| String::from("Subnet ID out of bounds, could not set subnet ID"))?;
// insert the bitfield into the ENR record
let _ = self
.discovery
.enr_insert(BITFIELD_ENR_KEY, current_bitfield.as_ssz_bytes());
Ok(())
}
/// Updates the `eth2` field of our local ENR. /// Updates the `eth2` field of our local ENR.
pub fn update_eth2_enr(&mut self, enr_fork_id: EnrForkId) { 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 // to avoid having a reference to the spec constant, for the logging we assume
@ -180,7 +232,7 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
let _ = self let _ = self
.discovery .discovery
.enr_insert("eth2".into(), enr_fork_id.as_ssz_bytes()) .enr_insert(ETH2_ENR_KEY.into(), enr_fork_id.as_ssz_bytes())
.map_err(|e| { .map_err(|e| {
warn!( warn!(
self.log, self.log,
@ -190,6 +242,39 @@ impl<TSubstream, TSpec: EthSpec> Discovery<TSubstream, TSpec> {
}); });
} }
/// A request to find peers on a given subnet.
// TODO: This logic should be improved with added sophistication in peer management
// This currently checks for currently connected peers and if we don't have
// PEERS_WANTED_BEFORE_DISCOVERY connected to a given subnet we search for more.
pub fn peers_request(&mut self, subnet_id: SubnetId) {
// TODO: Add PeerManager struct to do this loop for us
let peers_on_subnet = self
.network_globals
.connected_peer_set
.read()
.values()
.fold(0, |found_peers, peer_info| {
if peer_info.on_subnet(subnet_id) {
found_peers + 1
} else {
found_peers
}
});
if peers_on_subnet < TARGET_SUBNET_PEERS {
debug!(self.log, "Searching for peers for subnet";
"subnet_id" => *subnet_id,
"connected_peers_on_subnet" => peers_on_subnet,
"target_subnet_peers" => TARGET_SUBNET_PEERS
);
// TODO: Update to predicate search
self.find_peers();
}
}
/* Internal Functions */
/// 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
@ -217,11 +302,35 @@ where
} }
fn inject_connected(&mut self, peer_id: PeerId, _endpoint: ConnectedPoint) { fn inject_connected(&mut self, peer_id: PeerId, _endpoint: ConnectedPoint) {
// TODO: Search for a known ENR once discv5 is updated. // Find ENR info about a peer if possible.
let mut peer_info = PeerInfo::new();
if let Some(enr) = self.discovery.enr_of_peer(&peer_id) {
let bitfield = match enr.get(BITFIELD_ENR_KEY) {
Some(bitfield_bytes) => {
match EnrBitfield::<TSpec>::from_ssz_bytes(bitfield_bytes) {
Ok(bitfield) => bitfield,
Err(e) => {
warn!(self.log, "Peer had invalid ENR bitfield";
"peer_id" => format!("{}", peer_id),
"error" => format!("{:?}", e));
return;
}
}
}
None => {
warn!(self.log, "Peer has no ENR bitfield";
"peer_id" => format!("{}", peer_id));
return;
}
};
peer_info.enr_bitfield = Some(bitfield);
}
self.network_globals self.network_globals
.connected_peer_set .connected_peer_set
.write() .write()
.insert(peer_id, PeerInfo::new()); .insert(peer_id, peer_info);
// TODO: Drop peers if over max_peer limit // TODO: Drop peers if over max_peer limit
metrics::inc_counter(&metrics::PEER_CONNECT_EVENT_COUNT); metrics::inc_counter(&metrics::PEER_CONNECT_EVENT_COUNT);

View File

@ -24,7 +24,7 @@ use std::io::{Error, ErrorKind};
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::timer::DelayQueue; use tokio::timer::DelayQueue;
use types::EthSpec; use types::{EnrForkId, EthSpec};
type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>; type Libp2pStream = Boxed<(PeerId, StreamMuxerBox), Error>;
type Libp2pBehaviour<TSpec> = Behaviour<Substream<StreamMuxerBox>, TSpec>; type Libp2pBehaviour<TSpec> = Behaviour<Substream<StreamMuxerBox>, TSpec>;
@ -56,6 +56,7 @@ pub struct Service<TSpec: EthSpec> {
impl<TSpec: EthSpec> Service<TSpec> { impl<TSpec: EthSpec> Service<TSpec> {
pub fn new( pub fn new(
config: &NetworkConfig, config: &NetworkConfig,
enr_fork_id: EnrForkId,
log: slog::Logger, log: slog::Logger,
) -> error::Result<(Arc<NetworkGlobals<TSpec>>, Self)> { ) -> error::Result<(Arc<NetworkGlobals<TSpec>>, Self)> {
trace!(log, "Libp2p Service starting"); trace!(log, "Libp2p Service starting");
@ -81,7 +82,13 @@ impl<TSpec: EthSpec> Service<TSpec> {
// Set up the transport - tcp/ws with noise/secio and mplex/yamux // Set up the transport - tcp/ws with noise/secio and mplex/yamux
let transport = build_transport(local_keypair.clone()); let transport = build_transport(local_keypair.clone());
// Lighthouse network behaviour // Lighthouse network behaviour
let behaviour = Behaviour::new(&local_keypair, config, network_globals.clone(), &log)?; let behaviour = Behaviour::new(
&local_keypair,
config,
network_globals.clone(),
enr_fork_id,
&log,
)?;
Swarm::new(transport, behaviour, local_peer_id.clone()) Swarm::new(transport, behaviour, local_peer_id.clone())
}; };

View File

@ -119,6 +119,12 @@ impl Into<String> for GossipTopic {
} }
} }
impl From<SubnetId> for GossipKind {
fn from(subnet_id: SubnetId) -> Self {
GossipKind::CommitteeIndex(subnet_id)
}
}
// helper functions // helper functions
// Determines if a string is a committee topic. // Determines if a string is a committee topic.

View File

@ -5,7 +5,7 @@ use eth2_libp2p::NetworkConfig;
use eth2_libp2p::Service as LibP2PService; use eth2_libp2p::Service as LibP2PService;
use slog::{debug, error, o, Drain}; use slog::{debug, error, o, Drain};
use std::time::Duration; use std::time::Duration;
use types::MinimalEthSpec; use types::{EnrForkId, MinimalEthSpec};
type E = MinimalEthSpec; type E = MinimalEthSpec;
use tempdir::TempDir; use tempdir::TempDir;
@ -52,7 +52,7 @@ pub fn build_libp2p_instance(
) -> LibP2PService<E> { ) -> LibP2PService<E> {
let config = build_config(port, boot_nodes, secret_key); let config = build_config(port, boot_nodes, secret_key);
// launch libp2p service // launch libp2p service
LibP2PService::new(&config, log.clone()) LibP2PService::new(&config, EnrForkId::default(), log.clone())
.expect("should build libp2p instance") .expect("should build libp2p instance")
.1 .1
} }

View File

@ -1,7 +1,7 @@
#![cfg(test)] #![cfg(test)]
use crate::behaviour::{Behaviour, BehaviourEvent}; use crate::behaviour::{Behaviour, BehaviourEvent};
use crate::multiaddr::Protocol; use crate::multiaddr::Protocol;
use ::types::MinimalEthSpec; use ::types::{EnrForkId, MinimalEthSpec};
use eth2_libp2p::*; use eth2_libp2p::*;
use futures::prelude::*; use futures::prelude::*;
use libp2p::core::identity::Keypair; use libp2p::core::identity::Keypair;
@ -42,7 +42,13 @@ fn build_secio_swarm(
// Set up the transport - tcp/ws with secio and mplex/yamux // Set up the transport - tcp/ws with secio and mplex/yamux
let transport = build_secio_transport(local_keypair.clone()); let transport = build_secio_transport(local_keypair.clone());
// Lighthouse network behaviour // Lighthouse network behaviour
let behaviour = Behaviour::new(&local_keypair, config, network_globals.clone(), &log)?; let behaviour = Behaviour::new(
&local_keypair,
config,
network_globals.clone(),
EnrForkId::default(),
&log,
)?;
Swarm::new(transport, behaviour, local_peer_id.clone()) Swarm::new(transport, behaviour, local_peer_id.clone())
}; };
@ -122,7 +128,7 @@ fn test_secio_noise_fallback() {
let log = common::build_log(log_level, enable_logging); let log = common::build_log(log_level, enable_logging);
let noisy_config = common::build_config(56010, vec![], None); let noisy_config = common::build_config(56010, vec![], None);
let mut noisy_node = Service::new(&noisy_config, log.clone()) let mut noisy_node = Service::new(&noisy_config, EnrForkId::default(), log.clone())
.expect("should build a libp2p instance") .expect("should build a libp2p instance")
.1; .1;

View File

@ -3,19 +3,17 @@
//! determines whether attestations should be aggregated and/or passed to the beacon node. //! determines whether attestations should be aggregated and/or passed to the beacon node.
use beacon_chain::{BeaconChain, BeaconChainTypes}; use beacon_chain::{BeaconChain, BeaconChainTypes};
use eth2_libp2p::{types::GossipKind, NetworkGlobals}; use eth2_libp2p::{types::GossipKind, MessageId, NetworkGlobals, PeerId};
use futures::prelude::*; use futures::prelude::*;
use hashmap_delay::HashSetDelay; use hashmap_delay::HashSetDelay;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use rest_types::ValidatorSubscription; use rest_types::ValidatorSubscription;
use slog::{crit, debug, error, o, warn}; use slog::{crit, debug, error, o, warn};
use slot_clock::SlotClock; use slot_clock::SlotClock;
use std::boxed::Box;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use types::{Attestation, SubnetId}; use types::{Attestation, EthSpec, SignedAggregateAndProof, Slot, SubnetId};
use types::{EthSpec, Slot};
/// The minimum number of slots ahead that we attempt to discover peers for a subscription. If the /// The minimum number of slots ahead that we attempt to discover peers for a subscription. If the
/// slot is less than this number, skip the peer discovery process. /// slot is less than this number, skip the peer discovery process.
@ -44,6 +42,8 @@ pub enum AttServiceMessage {
EnrRemove(SubnetId), EnrRemove(SubnetId),
/// Discover peers for a particular subnet. /// Discover peers for a particular subnet.
DiscoverPeers(SubnetId), DiscoverPeers(SubnetId),
/// Propagate an attestation if it's deemed valid.
Propagate(PeerId, MessageId),
} }
pub struct AttestationService<T: BeaconChainTypes> { pub struct AttestationService<T: BeaconChainTypes> {
@ -152,11 +152,29 @@ impl<T: BeaconChainTypes> AttestationService<T> {
Ok(()) Ok(())
} }
pub fn handle_attestation( /// Handles un-aggregated attestations from the network.
pub fn handle_unaggregated_attestation(
&mut self, &mut self,
message_id: MessageId,
peer_id: PeerId,
subnet: SubnetId, subnet: SubnetId,
attestation: Box<Attestation<T::EthSpec>>, attestation: Attestation<T::EthSpec>,
) { ) {
// TODO: Handle attestation processing
self.events
.push_back(AttServiceMessage::Propagate(peer_id, message_id));
}
/// Handles aggregate attestations from the network.
pub fn handle_aggregate_attestation(
&mut self,
message_id: MessageId,
peer_id: PeerId,
attestation: SignedAggregateAndProof<T::EthSpec>,
) {
// TODO: Handle attestation processing
self.events
.push_back(AttServiceMessage::Propagate(peer_id, message_id));
} }
/* Internal private functions */ /* Internal private functions */
@ -231,6 +249,12 @@ impl<T: BeaconChainTypes> AttestationService<T> {
self.discover_peers self.discover_peers
.insert_at((subnet_id, subscription_slot), duration_to_discover); .insert_at((subnet_id, subscription_slot), duration_to_discover);
} }
} else {
// TODO: Send the time frame needed to have a peer connected, so that we can
// maintain peers for a least this duration.
// We may want to check the global PeerInfo to see estimated timeouts for each
// peer before they can be removed.
return Err("Not enough time for a discovery search");
} }
Ok(()) Ok(())
} }

View File

@ -0,0 +1,55 @@
/// Process a gossip message declaring a new attestation.
///
/// Not currently implemented.
pub fn on_attestation_gossip(&mut self, _peer_id: PeerId, _msg: Attestation<T::EthSpec>) {
// TODO: Handle subnet gossip
/*
match self.chain.process_attestation(msg.clone()) {
Ok(outcome) => match outcome {
AttestationProcessingOutcome::Processed => {
debug!(
self.log,
"Processed attestation";
"source" => "gossip",
"peer" => format!("{:?}",peer_id),
"block_root" => format!("{}", msg.data.beacon_block_root),
"slot" => format!("{}", msg.data.slot),
);
}
AttestationProcessingOutcome::UnknownHeadBlock { beacon_block_root } => {
// TODO: Maintain this attestation and re-process once sync completes
trace!(
self.log,
"Attestation for unknown block";
"peer_id" => format!("{:?}", peer_id),
"block" => format!("{}", beacon_block_root)
);
// we don't know the block, get the sync manager to handle the block lookup
self.send_to_sync(SyncMessage::UnknownBlockHash(peer_id, beacon_block_root));
}
AttestationProcessingOutcome::FutureEpoch { .. }
| AttestationProcessingOutcome::PastEpoch { .. }
| AttestationProcessingOutcome::UnknownTargetRoot { .. }
| AttestationProcessingOutcome::FinalizedSlot { .. } => {} // ignore the attestation
AttestationProcessingOutcome::Invalid { .. }
| AttestationProcessingOutcome::EmptyAggregationBitfield { .. }
| AttestationProcessingOutcome::AttestsToFutureBlock { .. }
| AttestationProcessingOutcome::InvalidSignature
| AttestationProcessingOutcome::NoCommitteeForSlotAndIndex { .. }
| AttestationProcessingOutcome::BadTargetEpoch { .. } => {
// the peer has sent a bad attestation. Remove them.
self.network.disconnect(peer_id, GoodbyeReason::Fault);
}
},
Err(_) => {
// error is logged during the processing therefore no error is logged here
trace!(
self.log,
"Erroneous gossip attestation ssz";
"ssz" => format!("0x{}", hex::encode(msg.as_ssz_bytes())),
);
}
};
*/
}

View File

@ -16,7 +16,7 @@ use eth2_libp2p::{
use futures::future::Future; use futures::future::Future;
use futures::stream::Stream; use futures::stream::Stream;
use processor::Processor; use processor::Processor;
use slog::{debug, o, trace, warn}; use slog::{crit, debug, o, trace, warn};
use std::sync::Arc; use std::sync::Arc;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use types::EthSpec; use types::EthSpec;
@ -224,19 +224,6 @@ impl<T: BeaconChainTypes> Router<T> {
} }
self.processor.on_block_gossip(peer_id, block); self.processor.on_block_gossip(peer_id, block);
} }
PubsubData::AggregateAndProofAttestation(_agg_attestation) => {
// TODO: Handle propagation conditions
self.propagate_message(id, peer_id);
// TODO Handle aggregate attestion
// self.processor
// .on_attestation_gossip(peer_id.clone(), &agg_attestation);
}
PubsubData::Attestation(boxed_shard_attestation) => {
// TODO: Handle propagation conditions
self.propagate_message(id, peer_id.clone());
self.processor
.on_attestation_gossip(peer_id, boxed_shard_attestation.1);
}
PubsubData::VoluntaryExit(_exit) => { PubsubData::VoluntaryExit(_exit) => {
// TODO: Apply more sophisticated validation // TODO: Apply more sophisticated validation
self.propagate_message(id, peer_id.clone()); self.propagate_message(id, peer_id.clone());
@ -255,6 +242,19 @@ impl<T: BeaconChainTypes> Router<T> {
// TODO: Handle attester slashings // TODO: Handle attester slashings
debug!(self.log, "Received an attester slashing"; "peer_id" => format!("{}", peer_id) ); debug!(self.log, "Received an attester slashing"; "peer_id" => format!("{}", peer_id) );
} }
// Attestations should never reach the router.
PubsubData::AggregateAndProofAttestation(_agg_attestation) => {
crit!(
self.log,
"Attestations should always be handled by the attestation service"
);
}
PubsubData::Attestation(_boxed_subnet_attestation) => {
crit!(
self.log,
"Attestations should always be handled by the attestation service"
);
}
} }
} }

View File

@ -1,8 +1,6 @@
use crate::service::NetworkMessage; use crate::service::NetworkMessage;
use crate::sync::SyncMessage; use crate::sync::SyncMessage;
use beacon_chain::{ use beacon_chain::{BeaconChain, BeaconChainTypes, BlockProcessingOutcome};
AttestationProcessingOutcome, BeaconChain, BeaconChainTypes, BlockProcessingOutcome,
};
use eth2_libp2p::rpc::methods::*; use eth2_libp2p::rpc::methods::*;
use eth2_libp2p::rpc::{RPCEvent, RPCRequest, RPCResponse, RequestId}; use eth2_libp2p::rpc::{RPCEvent, RPCRequest, RPCResponse, RequestId};
use eth2_libp2p::PeerId; use eth2_libp2p::PeerId;
@ -553,61 +551,6 @@ impl<T: BeaconChainTypes> Processor<T> {
// TODO: Update with correct block gossip checking // TODO: Update with correct block gossip checking
true true
} }
/// Process a gossip message declaring a new attestation.
///
/// Not currently implemented.
pub fn on_attestation_gossip(&mut self, _peer_id: PeerId, _msg: Attestation<T::EthSpec>) {
// TODO: Handle subnet gossip
/*
match self.chain.process_attestation(msg.clone()) {
Ok(outcome) => match outcome {
AttestationProcessingOutcome::Processed => {
debug!(
self.log,
"Processed attestation";
"source" => "gossip",
"peer" => format!("{:?}",peer_id),
"block_root" => format!("{}", msg.data.beacon_block_root),
"slot" => format!("{}", msg.data.slot),
);
}
AttestationProcessingOutcome::UnknownHeadBlock { beacon_block_root } => {
// TODO: Maintain this attestation and re-process once sync completes
trace!(
self.log,
"Attestation for unknown block";
"peer_id" => format!("{:?}", peer_id),
"block" => format!("{}", beacon_block_root)
);
// we don't know the block, get the sync manager to handle the block lookup
self.send_to_sync(SyncMessage::UnknownBlockHash(peer_id, beacon_block_root));
}
AttestationProcessingOutcome::FutureEpoch { .. }
| AttestationProcessingOutcome::PastEpoch { .. }
| AttestationProcessingOutcome::UnknownTargetRoot { .. }
| AttestationProcessingOutcome::FinalizedSlot { .. } => {} // ignore the attestation
AttestationProcessingOutcome::Invalid { .. }
| AttestationProcessingOutcome::EmptyAggregationBitfield { .. }
| AttestationProcessingOutcome::AttestsToFutureBlock { .. }
| AttestationProcessingOutcome::InvalidSignature
| AttestationProcessingOutcome::NoCommitteeForSlotAndIndex { .. }
| AttestationProcessingOutcome::BadTargetEpoch { .. } => {
// the peer has sent a bad attestation. Remove them.
self.network.disconnect(peer_id, GoodbyeReason::Fault);
}
},
Err(_) => {
// error is logged during the processing therefore no error is logged here
trace!(
self.log,
"Erroneous gossip attestation ssz";
"ssz" => format!("0x{}", hex::encode(msg.as_ssz_bytes())),
);
}
};
*/
}
} }
/// Build a `StatusMessage` representing the state of the given `beacon_chain`. /// Build a `StatusMessage` representing the state of the given `beacon_chain`.

View File

@ -8,7 +8,7 @@ use crate::{
use beacon_chain::{BeaconChain, BeaconChainTypes}; use beacon_chain::{BeaconChain, BeaconChainTypes};
use eth2_libp2p::Service as LibP2PService; use eth2_libp2p::Service as LibP2PService;
use eth2_libp2p::{rpc::RPCRequest, Enr, Libp2pEvent, MessageId, NetworkGlobals, PeerId, Swarm}; use eth2_libp2p::{rpc::RPCRequest, Enr, Libp2pEvent, MessageId, NetworkGlobals, PeerId, Swarm};
use eth2_libp2p::{PubsubMessage, RPCEvent}; use eth2_libp2p::{PubsubData, PubsubMessage, RPCEvent};
use futures::prelude::*; use futures::prelude::*;
use futures::Stream; use futures::Stream;
use rest_types::ValidatorSubscription; use rest_types::ValidatorSubscription;
@ -55,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: &mut NetworkConfig, config: &NetworkConfig,
executor: &TaskExecutor, executor: &TaskExecutor,
network_log: slog::Logger, network_log: slog::Logger,
) -> error::Result<( ) -> error::Result<(
@ -77,8 +77,8 @@ impl<T: BeaconChainTypes> NetworkService<T> {
let propagation_percentage = config.propagation_percentage; let propagation_percentage = config.propagation_percentage;
// set the local enr_fork_id // build the current enr_fork_id for adding to our local ENR
config.enr_fork_id = beacon_chain let enr_fork_id = beacon_chain
.enr_fork_id() .enr_fork_id()
.map_err(|e| format!("Could not get the current ENR fork version: {:?}", e))?; .map_err(|e| format!("Could not get the current ENR fork version: {:?}", e))?;
@ -88,7 +88,8 @@ impl<T: BeaconChainTypes> NetworkService<T> {
.map_err(|e| format!("Could not get the next fork update duration: {:?}", e))?; .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, enr_fork_id, network_log.clone())?;
for enr in load_dht::<T::Store, T::EthSpec>(store.clone()) { for enr in load_dht::<T::Store, T::EthSpec>(store.clone()) {
libp2p.swarm.add_enr(enr); libp2p.swarm.add_enr(enr);
@ -262,11 +263,26 @@ fn spawn_service<T: BeaconChainTypes>(
while let Ok(Async::Ready(Some(attestation_service_message))) = service.attestation_service.poll() { while let Ok(Async::Ready(Some(attestation_service_message))) = service.attestation_service.poll() {
match attestation_service_message { match attestation_service_message {
// TODO: Implement // TODO: Implement
AttServiceMessage::Subscribe(_subnet) => { }, AttServiceMessage::Subscribe(subnet_id) => {
AttServiceMessage::Unsubscribe(_subnet) => { }, service.libp2p.swarm.subscribe_to_subnet(subnet_id);
AttServiceMessage::EnrAdd(_subnet) => { }, },
AttServiceMessage::EnrRemove(_subnet) => { }, AttServiceMessage::Unsubscribe(subnet_id) => {
AttServiceMessage::DiscoverPeers(_subnet) => { }, service.libp2p.swarm.subscribe_to_subnet(subnet_id);
},
AttServiceMessage::EnrAdd(subnet_id) => {
service.libp2p.swarm.update_enr_subnet(subnet_id, true);
},
AttServiceMessage::EnrRemove(subnet_id) => {
service.libp2p.swarm.update_enr_subnet(subnet_id, false);
},
AttServiceMessage::DiscoverPeers(subnet_id) => {
service.libp2p.swarm.peers_request(subnet_id);
},
AttServiceMessage::Propagate(source, message_id) => {
service.libp2p
.swarm
.propagate_message(&source, message_id);
}
} }
} }
@ -276,8 +292,6 @@ fn spawn_service<T: BeaconChainTypes>(
match service.libp2p.poll() { match service.libp2p.poll() {
Ok(Async::Ready(Some(event))) => match event { Ok(Async::Ready(Some(event))) => match event {
Libp2pEvent::RPC(peer_id, rpc_event) => { Libp2pEvent::RPC(peer_id, rpc_event) => {
// trace!(log, "Received RPC"; "rpc" => format!("{}", rpc_event));
// if we received a Goodbye message, drop and ban the peer // if we received a Goodbye message, drop and ban the peer
if let RPCEvent::Request(_, RPCRequest::Goodbye(_)) = rpc_event { if let RPCEvent::Request(_, RPCRequest::Goodbye(_)) = rpc_event {
peers_to_ban.push(peer_id.clone()); peers_to_ban.push(peer_id.clone());
@ -304,9 +318,24 @@ fn spawn_service<T: BeaconChainTypes>(
message, message,
.. ..
} => { } => {
service.router_send
.try_send(RouterMessage::PubsubMessage(id, source, message)) match message.data {
.map_err(|_| { debug!(log, "Failed to send pubsub message to router");})?; // attestation information gets processed in the attestation service
PubsubData::AggregateAndProofAttestation(signed_aggregate_and_proof) => {
service.attestation_service.handle_aggregate_attestation(id, source, *signed_aggregate_and_proof);
},
PubsubData::Attestation(subnet_and_attestation) => {
let subnet = subnet_and_attestation.0;
let attestation = subnet_and_attestation.1;
service.attestation_service.handle_unaggregated_attestation(id, source, subnet, attestation);
}
_ => {
// all else is sent to the router
service.router_send
.try_send(RouterMessage::PubsubMessage(id, source, message))
.map_err(|_| { debug!(log, "Failed to send pubsub message to router");})?;
}
}
} }
Libp2pEvent::PeerSubscribed(_, _) => {} Libp2pEvent::PeerSubscribed(_, _) => {}
}, },