mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-14 10:22:38 +00:00
Add first changes to syncing logic
- Adds testing framework - Breaks out new `NetworkContext` object
This commit is contained in:
@@ -7,6 +7,8 @@ use beacon_chain::{
|
||||
types::{BeaconState, ChainSpec},
|
||||
CheckPoint,
|
||||
};
|
||||
use libp2p::HelloMessage;
|
||||
use types::{Epoch, Hash256, Slot};
|
||||
|
||||
/// The network's API to the beacon chain.
|
||||
pub trait BeaconChain: Send + Sync {
|
||||
@@ -14,9 +16,19 @@ pub trait BeaconChain: Send + Sync {
|
||||
|
||||
fn get_state(&self) -> RwLockReadGuard<BeaconState>;
|
||||
|
||||
fn slot(&self) -> Slot;
|
||||
|
||||
fn head(&self) -> RwLockReadGuard<CheckPoint>;
|
||||
|
||||
fn best_slot(&self) -> Slot;
|
||||
|
||||
fn best_block_root(&self) -> Hash256;
|
||||
|
||||
fn finalized_head(&self) -> RwLockReadGuard<CheckPoint>;
|
||||
|
||||
fn finalized_epoch(&self) -> Epoch;
|
||||
|
||||
fn hello_message(&self) -> HelloMessage;
|
||||
}
|
||||
|
||||
impl<T, U, F> BeaconChain for RawBeaconChain<T, U, F>
|
||||
@@ -33,11 +45,40 @@ where
|
||||
self.state.read()
|
||||
}
|
||||
|
||||
fn slot(&self) -> Slot {
|
||||
self.get_state().slot
|
||||
}
|
||||
|
||||
fn head(&self) -> RwLockReadGuard<CheckPoint> {
|
||||
self.head()
|
||||
}
|
||||
|
||||
fn finalized_epoch(&self) -> Epoch {
|
||||
self.get_state().finalized_epoch
|
||||
}
|
||||
|
||||
fn finalized_head(&self) -> RwLockReadGuard<CheckPoint> {
|
||||
self.finalized_head()
|
||||
}
|
||||
|
||||
fn best_slot(&self) -> Slot {
|
||||
self.head().beacon_block.slot
|
||||
}
|
||||
|
||||
fn best_block_root(&self) -> Hash256 {
|
||||
self.head().beacon_block_root
|
||||
}
|
||||
|
||||
fn hello_message(&self) -> HelloMessage {
|
||||
let spec = self.get_spec();
|
||||
let state = self.get_state();
|
||||
|
||||
HelloMessage {
|
||||
network_id: spec.network_id,
|
||||
latest_finalized_root: state.finalized_root,
|
||||
latest_finalized_epoch: state.finalized_epoch,
|
||||
best_root: self.best_block_root(),
|
||||
best_slot: self.best_slot(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/// This crate provides the network server for Lighthouse.
|
||||
pub mod beacon_chain;
|
||||
pub mod error;
|
||||
mod message_handler;
|
||||
mod service;
|
||||
pub mod message_handler;
|
||||
pub mod service;
|
||||
pub mod sync;
|
||||
|
||||
pub use libp2p::NetworkConfig;
|
||||
|
||||
@@ -5,32 +5,28 @@ use crate::sync::SimpleSync;
|
||||
use crossbeam_channel::{unbounded as channel, Sender};
|
||||
use futures::future;
|
||||
use libp2p::{
|
||||
rpc::{RPCMethod, RPCRequest, RPCResponse},
|
||||
rpc::{RPCRequest, RPCResponse},
|
||||
HelloMessage, PeerId, RPCEvent,
|
||||
};
|
||||
use slog::debug;
|
||||
use slog::warn;
|
||||
use slog::{debug, trace};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::time::Instant;
|
||||
|
||||
/// Timeout for RPC requests.
|
||||
const REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
// const REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
/// Timeout before banning a peer for non-identification.
|
||||
const HELLO_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
// const HELLO_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
/// Handles messages received from the network and client and organises syncing.
|
||||
pub struct MessageHandler {
|
||||
/// Currently loaded and initialised beacon chain.
|
||||
chain: Arc<BeaconChain>,
|
||||
_chain: Arc<BeaconChain>,
|
||||
/// The syncing framework.
|
||||
sync: SimpleSync,
|
||||
/// The network channel to relay messages to the Network service.
|
||||
network_send: crossbeam_channel::Sender<NetworkMessage>,
|
||||
/// A mapping of peers and the RPC id we have sent an RPC request to.
|
||||
requests: HashMap<(PeerId, u64), Instant>,
|
||||
/// A counter of request id for each peer.
|
||||
request_ids: HashMap<PeerId, u64>,
|
||||
/// The context required to send messages to, and process messages from peers.
|
||||
network_context: NetworkContext,
|
||||
/// The `MessageHandler` logger.
|
||||
log: slog::Logger,
|
||||
}
|
||||
@@ -65,13 +61,9 @@ impl MessageHandler {
|
||||
let sync = SimpleSync::new(beacon_chain.clone(), &log);
|
||||
|
||||
let mut handler = MessageHandler {
|
||||
// TODO: The handler may not need a chain, perhaps only sync?
|
||||
chain: beacon_chain.clone(),
|
||||
_chain: beacon_chain.clone(),
|
||||
sync,
|
||||
network_send,
|
||||
requests: HashMap::new(),
|
||||
request_ids: HashMap::new(),
|
||||
|
||||
network_context: NetworkContext::new(network_send, log.clone()),
|
||||
log: log.clone(),
|
||||
};
|
||||
|
||||
@@ -93,8 +85,7 @@ impl MessageHandler {
|
||||
match message {
|
||||
// we have initiated a connection to a peer
|
||||
HandlerMessage::PeerDialed(peer_id) => {
|
||||
let id = self.generate_request_id(&peer_id);
|
||||
self.send_hello(peer_id, id, true);
|
||||
self.sync.on_connect(&peer_id, &mut self.network_context);
|
||||
}
|
||||
// we have received an RPC message request/response
|
||||
HandlerMessage::RPC(peer_id, rpc_event) => {
|
||||
@@ -118,9 +109,11 @@ impl MessageHandler {
|
||||
|
||||
/// A new RPC request has been received from the network.
|
||||
fn handle_rpc_request(&mut self, peer_id: PeerId, id: u64, request: RPCRequest) {
|
||||
// TODO: ensure the id is legit
|
||||
match request {
|
||||
RPCRequest::Hello(hello_message) => {
|
||||
self.handle_hello_request(peer_id, id, hello_message)
|
||||
self.sync
|
||||
.on_hello(&peer_id, hello_message, &mut self.network_context)
|
||||
}
|
||||
// TODO: Handle all requests
|
||||
_ => {}
|
||||
@@ -131,7 +124,12 @@ impl MessageHandler {
|
||||
// we match on id and ignore responses past the timeout.
|
||||
fn handle_rpc_response(&mut self, peer_id: PeerId, id: u64, response: RPCResponse) {
|
||||
// if response id is related to a request, ignore (likely RPC timeout)
|
||||
if self.requests.remove(&(peer_id.clone(), id)).is_none() {
|
||||
if self
|
||||
.network_context
|
||||
.requests
|
||||
.remove(&(peer_id.clone(), id))
|
||||
.is_none()
|
||||
{
|
||||
debug!(self.log, "Unrecognized response from peer: {:?}", peer_id);
|
||||
return;
|
||||
}
|
||||
@@ -145,16 +143,10 @@ impl MessageHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle a HELLO RPC request message.
|
||||
fn handle_hello_request(&mut self, peer_id: PeerId, id: u64, hello_message: HelloMessage) {
|
||||
// send back a HELLO message
|
||||
self.send_hello(peer_id.clone(), id, false);
|
||||
// validate the peer
|
||||
self.validate_hello(peer_id, hello_message);
|
||||
}
|
||||
|
||||
/// Validate a HELLO RPC message.
|
||||
fn validate_hello(&mut self, peer_id: PeerId, message: HelloMessage) {
|
||||
self.sync
|
||||
.on_hello(&peer_id, message.clone(), &mut self.network_context);
|
||||
// validate the peer
|
||||
if !self.sync.validate_peer(peer_id.clone(), message) {
|
||||
debug!(
|
||||
@@ -164,8 +156,68 @@ impl MessageHandler {
|
||||
//TODO: block/ban the peer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* General RPC helper functions */
|
||||
pub struct NetworkContext {
|
||||
/// The network channel to relay messages to the Network service.
|
||||
network_send: crossbeam_channel::Sender<NetworkMessage>,
|
||||
/// A mapping of peers and the RPC id we have sent an RPC request to.
|
||||
requests: HashMap<(PeerId, u64), Instant>,
|
||||
/// A counter of request id for each peer.
|
||||
request_ids: HashMap<PeerId, u64>,
|
||||
/// The `MessageHandler` logger.
|
||||
log: slog::Logger,
|
||||
}
|
||||
|
||||
impl NetworkContext {
|
||||
pub fn new(network_send: crossbeam_channel::Sender<NetworkMessage>, log: slog::Logger) -> Self {
|
||||
Self {
|
||||
network_send,
|
||||
requests: HashMap::new(),
|
||||
request_ids: HashMap::new(),
|
||||
log,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send_rpc_request(&mut self, peer_id: PeerId, rpc_request: RPCRequest) {
|
||||
let id = self.generate_request_id(&peer_id);
|
||||
self.send_rpc_event(
|
||||
peer_id,
|
||||
RPCEvent::Request {
|
||||
id,
|
||||
method_id: rpc_request.method_id(),
|
||||
body: rpc_request,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
pub fn send_rpc_response(&mut self, peer_id: PeerId, rpc_response: RPCResponse) {
|
||||
let id = self.generate_request_id(&peer_id);
|
||||
self.send_rpc_event(
|
||||
peer_id,
|
||||
RPCEvent::Response {
|
||||
id,
|
||||
method_id: rpc_response.method_id(),
|
||||
result: rpc_response,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn send_rpc_event(&self, peer_id: PeerId, rpc_event: RPCEvent) {
|
||||
self.send(peer_id, OutgoingMessage::RPC(rpc_event))
|
||||
}
|
||||
|
||||
fn send(&self, peer_id: PeerId, outgoing_message: OutgoingMessage) {
|
||||
self.network_send
|
||||
.send(NetworkMessage::Send(peer_id, outgoing_message))
|
||||
.unwrap_or_else(|_| {
|
||||
warn!(
|
||||
self.log,
|
||||
"Could not send RPC message to the network service"
|
||||
)
|
||||
});
|
||||
//
|
||||
}
|
||||
|
||||
/// Generates a new request id for a peer.
|
||||
fn generate_request_id(&mut self, peer_id: &PeerId) -> u64 {
|
||||
@@ -185,41 +237,4 @@ impl MessageHandler {
|
||||
);
|
||||
id
|
||||
}
|
||||
|
||||
/// Sends a HELLO RPC request or response to a newly connected peer.
|
||||
//TODO: The boolean determines if sending request/respond, will be cleaner in the RPC re-write
|
||||
fn send_hello(&mut self, peer_id: PeerId, id: u64, is_request: bool) {
|
||||
let rpc_event = if is_request {
|
||||
RPCEvent::Request {
|
||||
id,
|
||||
method_id: RPCMethod::Hello.into(),
|
||||
body: RPCRequest::Hello(self.sync.generate_hello()),
|
||||
}
|
||||
} else {
|
||||
RPCEvent::Response {
|
||||
id,
|
||||
method_id: RPCMethod::Hello.into(),
|
||||
result: RPCResponse::Hello(self.sync.generate_hello()),
|
||||
}
|
||||
};
|
||||
|
||||
// send the hello request to the network
|
||||
trace!(self.log, "Sending HELLO message to peer {:?}", peer_id);
|
||||
self.send_rpc(peer_id, rpc_event);
|
||||
}
|
||||
|
||||
/// Sends an RPC request/response to the network server.
|
||||
fn send_rpc(&self, peer_id: PeerId, rpc_event: RPCEvent) {
|
||||
self.network_send
|
||||
.send(NetworkMessage::Send(
|
||||
peer_id,
|
||||
OutgoingMessage::RPC(rpc_event),
|
||||
))
|
||||
.unwrap_or_else(|_| {
|
||||
warn!(
|
||||
self.log,
|
||||
"Could not send RPC message to the network service"
|
||||
)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ use crossbeam_channel::{unbounded as channel, Sender, TryRecvError};
|
||||
use futures::prelude::*;
|
||||
use futures::sync::oneshot;
|
||||
use futures::Stream;
|
||||
use libp2p::rpc::RPCResponse;
|
||||
use libp2p::RPCEvent;
|
||||
use libp2p::Service as LibP2PService;
|
||||
use libp2p::{Libp2pEvent, PeerId};
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
use crate::beacon_chain::BeaconChain;
|
||||
use libp2p::rpc::HelloMessage;
|
||||
use crate::message_handler::{MessageHandler, NetworkContext};
|
||||
use crate::service::NetworkMessage;
|
||||
use crossbeam_channel::Sender;
|
||||
use libp2p::rpc::{HelloMessage, RPCMethod, RPCRequest, RPCResponse};
|
||||
use libp2p::PeerId;
|
||||
use slog::{debug, o};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use types::{Epoch, Hash256, Slot};
|
||||
|
||||
type NetworkSender = Sender<NetworkMessage>;
|
||||
|
||||
/// The number of slots that we can import blocks ahead of us, before going into full Sync mode.
|
||||
const SLOT_IMPORT_TOLERANCE: u64 = 100;
|
||||
|
||||
@@ -17,6 +22,32 @@ pub struct PeerSyncInfo {
|
||||
best_slot: Slot,
|
||||
}
|
||||
|
||||
impl PeerSyncInfo {
|
||||
pub fn is_on_chain(&self, chain: &Arc<BeaconChain>) -> bool {
|
||||
// TODO: make useful.
|
||||
true
|
||||
}
|
||||
|
||||
pub fn has_higher_finalized_epoch(&self, chain: &Arc<BeaconChain>) -> bool {
|
||||
self.latest_finalized_epoch > chain.get_state().finalized_epoch
|
||||
}
|
||||
|
||||
pub fn has_higher_best_slot(&self, chain: &Arc<BeaconChain>) -> bool {
|
||||
self.latest_finalized_epoch > chain.get_state().finalized_epoch
|
||||
}
|
||||
}
|
||||
|
||||
impl From<HelloMessage> for PeerSyncInfo {
|
||||
fn from(hello: HelloMessage) -> PeerSyncInfo {
|
||||
PeerSyncInfo {
|
||||
latest_finalized_root: hello.latest_finalized_root,
|
||||
latest_finalized_epoch: hello.latest_finalized_epoch,
|
||||
best_root: hello.best_root,
|
||||
best_slot: hello.best_slot,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The current syncing state.
|
||||
#[derive(PartialEq)]
|
||||
pub enum SyncState {
|
||||
@@ -60,17 +91,81 @@ impl SimpleSync {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_connect(&self, peer_id: &PeerId, network: &mut NetworkContext) {
|
||||
network.send_rpc_request(
|
||||
peer_id.clone(),
|
||||
RPCRequest::Hello(self.chain.hello_message()),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn on_hello_request(
|
||||
&self,
|
||||
peer_id: &PeerId,
|
||||
hello: HelloMessage,
|
||||
network: &mut NetworkContext,
|
||||
) {
|
||||
network.send_rpc_response(
|
||||
peer_id.clone(),
|
||||
RPCResponse::Hello(self.chain.hello_message()),
|
||||
);
|
||||
self.on_hello(peer_id, hello, network);
|
||||
}
|
||||
|
||||
pub fn on_hello(&self, peer_id: &PeerId, hello: HelloMessage, network: &mut NetworkContext) {
|
||||
// network id must match
|
||||
if hello.network_id != self.network_id {
|
||||
debug!(self.log, "Bad network id. Peer: {:?}", peer_id);
|
||||
return;
|
||||
}
|
||||
|
||||
let peer = PeerSyncInfo::from(hello);
|
||||
|
||||
/*
|
||||
if peer.has_higher_finalized_epoch(&self.chain) {
|
||||
// we need blocks
|
||||
let peer_slot = peer.latest_finalized_epoch.start_slot(spec.slots_per_epoch);
|
||||
let our_slot = self.chain.finalized_epoch();
|
||||
let required_slots = peer_slot - our_slot;
|
||||
} else {
|
||||
if !peer.is_on_chain(&self.chain) {
|
||||
return (true, responses);
|
||||
}
|
||||
//
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
// compare latest epoch and finalized root to see if they exist in our chain
|
||||
if peer_info.latest_finalized_epoch <= self.latest_finalized_epoch {
|
||||
// ensure their finalized root is in our chain
|
||||
// TODO: Get the finalized root at hello_message.latest_epoch and ensure they match
|
||||
//if (hello_message.latest_finalized_root == self.chain.get_state() {
|
||||
// return false;
|
||||
// }
|
||||
}
|
||||
|
||||
// the client is valid, add it to our list of known_peers and request sync if required
|
||||
// update peer list if peer already exists
|
||||
let peer_info = PeerSyncInfo::from(hello);
|
||||
|
||||
debug!(self.log, "Handshake successful. Peer: {:?}", peer_id);
|
||||
self.known_peers.insert(peer_id, peer_info);
|
||||
|
||||
// set state to sync
|
||||
if self.state == SyncState::Idle
|
||||
&& hello_message.best_slot > self.latest_slot + SLOT_IMPORT_TOLERANCE
|
||||
{
|
||||
self.state = SyncState::Downloading;
|
||||
//TODO: Start requesting blocks from known peers. Ideally in batches
|
||||
}
|
||||
|
||||
true
|
||||
*/
|
||||
}
|
||||
|
||||
/// Generates our current state in the form of a HELLO RPC message.
|
||||
pub fn generate_hello(&self) -> HelloMessage {
|
||||
let state = &self.chain.get_state();
|
||||
//TODO: Paul to verify the logic of these fields.
|
||||
HelloMessage {
|
||||
network_id: self.network_id,
|
||||
latest_finalized_root: state.finalized_root,
|
||||
latest_finalized_epoch: state.finalized_epoch,
|
||||
best_root: state.latest_block_roots[0], //TODO: build correct value as a beacon chain function
|
||||
best_slot: state.slot - 1,
|
||||
}
|
||||
self.chain.hello_message()
|
||||
}
|
||||
|
||||
pub fn validate_peer(&mut self, peer_id: PeerId, hello_message: HelloMessage) -> bool {
|
||||
|
||||
Reference in New Issue
Block a user