P2P server cleanup of disconnected peers. Mark those that don't respect the protocol as banned.

This commit is contained in:
Ignotus Peverell 2017-02-27 14:17:53 -08:00
parent 7c72ccec7c
commit d900f0b934
No known key found for this signature in database
GPG key ID: 99CD25F39F8F8211
6 changed files with 83 additions and 11 deletions

View file

@ -79,6 +79,18 @@ impl Seeder {
let mon_loop = Timer::default() let mon_loop = Timer::default()
.interval(time::Duration::from_secs(10)) .interval(time::Duration::from_secs(10))
.for_each(move |_| { .for_each(move |_| {
// maintenance step first, clean up p2p server peers and mark bans
// if needed
let disconnected = p2p_server.clean_peers();
for p in disconnected {
if p.is_banned() {
debug!("Marking peer {} as banned.", p.info.addr);
peer_store.update_state(p.info.addr, p2p::State::Banned);
}
}
// we don't have enough peers, getting more from db
if p2p_server.peer_count() < PEER_PREFERRED_COUNT { if p2p_server.peer_count() < PEER_PREFERRED_COUNT {
let mut peers = peer_store.find_peers(p2p::State::Healthy, let mut peers = peer_store.find_peers(p2p::State::Healthy,
p2p::UNKNOWN, p2p::UNKNOWN,
@ -91,7 +103,6 @@ impl Seeder {
} }
} }
Ok(()) Ok(())
// TODO clean disconnected server peers
}) })
.map_err(|e| e.to_string()); .map_err(|e| e.to_string());
Box::new(mon_loop) Box::new(mon_loop)

View file

@ -141,8 +141,9 @@ impl Connection {
data data
}) })
// write the data and make sure the future returns the right types // write the data and make sure the future returns the right types
.fold(writer, .fold(writer, |writer, data| {
|writer, data| write_all(writer, data).map_err(|e| Error::Connection(e)).map(|(writer, buf)| writer)); write_all(writer, data).map_err(|e| Error::Connection(e)).map(|(writer, buf)| writer)
});
Box::new(send_data) Box::new(send_data)
} }

View file

@ -24,6 +24,7 @@ use core::core::target::Difficulty;
use handshake::Handshake; use handshake::Handshake;
use types::*; use types::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum State { enum State {
Connected, Connected,
Disconnected, Disconnected,
@ -87,6 +88,7 @@ impl Peer {
let addr = self.info.addr; let addr = self.info.addr;
let state = self.state.clone(); let state = self.state.clone();
Box::new(self.proto.handle(conn, na).then(move |res| { Box::new(self.proto.handle(conn, na).then(move |res| {
// handle disconnection, standard disconnections aren't considered an error
let mut state = state.write().unwrap(); let mut state = state.write().unwrap();
match res { match res {
Ok(res) => { Ok(res) => {
@ -108,6 +110,18 @@ impl Peer {
})) }))
} }
/// Whether this peer is still connected.
pub fn is_connected(&self) -> bool {
let state = self.state.read().unwrap();
*state == State::Connected
}
/// Whether this peer has been banned.
pub fn is_banned(&self) -> bool {
let state = self.state.read().unwrap();
*state == State::Banned
}
/// Bytes sent and received by this peer to the remote peer. /// Bytes sent and received by this peer to the remote peer.
pub fn transmitted_bytes(&self) -> (u64, u64) { pub fn transmitted_bytes(&self) -> (u64, u64) {
self.proto.transmitted_bytes() self.proto.transmitted_bytes()

View file

@ -186,6 +186,24 @@ impl Server {
Box::new(request) Box::new(request)
} }
/// Have the server iterate over its peer list and prune all peers we have
/// lost connection to or have been deemed problematic. The removed peers
/// are returned.
pub fn clean_peers(&self) -> Vec<Arc<Peer>> {
let mut peers = self.peers.write().unwrap();
let (keep, rm) = peers.iter().fold((vec![], vec![]), |mut acc, ref p| {
if p.clone().is_connected() {
acc.0.push((*p).clone());
} else {
acc.1.push((*p).clone());
}
acc
});
*peers = keep;
rm
}
/// Returns the peer with the most worked branch, showing the highest total /// Returns the peer with the most worked branch, showing the highest total
/// difficulty. /// difficulty.
pub fn most_work_peer(&self) -> Option<Arc<Peer>> { pub fn most_work_peer(&self) -> Option<Arc<Peer>> {
@ -195,7 +213,7 @@ impl Server {
} }
let mut res = peers[0].clone(); let mut res = peers[0].clone();
for p in peers.deref() { for p in peers.deref() {
if res.info.total_difficulty < p.info.total_difficulty { if p.is_connected() && res.info.total_difficulty < p.info.total_difficulty {
res = (*p).clone(); res = (*p).clone();
} }
} }
@ -219,8 +237,10 @@ impl Server {
pub fn broadcast_block(&self, b: &core::Block) { pub fn broadcast_block(&self, b: &core::Block) {
let peers = self.peers.write().unwrap(); let peers = self.peers.write().unwrap();
for p in peers.deref() { for p in peers.deref() {
if let Err(e) = p.send_block(b) { if p.is_connected() {
debug!("Error sending block to peer: {:?}", e); if let Err(e) = p.send_block(b) {
debug!("Error sending block to peer: {:?}", e);
}
} }
} }
} }

View file

@ -18,7 +18,7 @@ use std::net::SocketAddr;
use num::FromPrimitive; use num::FromPrimitive;
use core::ser::{self, Readable, Writeable, Reader, Writer}; use core::ser::{self, Readable, Writeable, Reader, Writer};
use grin_store::{self, Error, to_key}; use grin_store::{self, Error, to_key, option_to_not_found};
use msg::SockAddr; use msg::SockAddr;
use types::Capabilities; use types::Capabilities;
@ -36,10 +36,16 @@ enum_from_primitive! {
} }
} }
/// Data stored for any given peer we've encountered.
pub struct PeerData { pub struct PeerData {
/// Network address of the peer.
pub addr: SocketAddr, pub addr: SocketAddr,
/// What capabilities the peer advertises. Unknown until a successful
/// connection.
pub capabilities: Capabilities, pub capabilities: Capabilities,
/// The peer user agent.
pub user_agent: String, pub user_agent: String,
/// State the peer has been detected with.
pub flags: State, pub flags: State,
} }
@ -74,11 +80,13 @@ impl Readable<PeerData> for PeerData {
} }
} }
/// Storage facility for peer data.
pub struct PeerStore { pub struct PeerStore {
db: grin_store::Store, db: grin_store::Store,
} }
impl PeerStore { impl PeerStore {
/// Instantiates a new peer store under the provided root path.
pub fn new(root_path: String) -> Result<PeerStore, Error> { pub fn new(root_path: String) -> Result<PeerStore, Error> {
let db = grin_store::Store::open(format!("{}/{}", root_path, STORE_SUBPATH).as_str())?; let db = grin_store::Store::open(format!("{}/{}", root_path, STORE_SUBPATH).as_str())?;
Ok(PeerStore { db: db }) Ok(PeerStore { db: db })
@ -89,6 +97,10 @@ impl PeerStore {
p) p)
} }
fn get_peer(&self, peer_addr: SocketAddr) -> Result<PeerData, Error> {
option_to_not_found(self.db.get_ser(&peer_key(peer_addr)[..]))
}
pub fn exists_peer(&self, peer_addr: SocketAddr) -> Result<bool, Error> { pub fn exists_peer(&self, peer_addr: SocketAddr) -> Result<bool, Error> {
self.db.exists(&to_key(PEER_PREFIX, &mut format!("{}", peer_addr).into_bytes())[..]) self.db.exists(&to_key(PEER_PREFIX, &mut format!("{}", peer_addr).into_bytes())[..])
} }
@ -111,4 +123,16 @@ impl PeerStore {
} }
peers peers
} }
/// Convenience method to load a peer data, update its status and save it
/// back.
pub fn update_state(&self, peer_addr: SocketAddr, new_state: State) -> Result<(), Error> {
let mut peer = self.get_peer(peer_addr)?;
peer.flags = new_state;
self.save_peer(&peer)
}
}
fn peer_key(peer_addr: SocketAddr) -> Vec<u8> {
to_key(PEER_PREFIX, &mut format!("{}", peer_addr).into_bytes())
} }

View file

@ -217,10 +217,12 @@ impl<T> Iterator for SerIterator<T>
} }
/// Build a db key from a prefix and a byte vector identifier. /// Build a db key from a prefix and a byte vector identifier.
pub fn to_key(prefix: u8, id: &mut Vec<u8>) -> &mut Vec<u8> { pub fn to_key(prefix: u8, k: &mut Vec<u8>) -> Vec<u8> {
id.insert(0, SEP); let mut res = Vec::with_capacity(k.len() + 2);
id.insert(0, prefix); res.push(prefix);
id res.push(SEP);
res.append(k);
res
} }
/// Build a db key from a prefix and a numeric identifier. /// Build a db key from a prefix and a numeric identifier.