Discovery and seeding of other peers. Relies either on a gist with IP addresses or a static list. Connects to a first list, sending peer request messages to discover more. Monitor the server to make sure there's always enough peer connections.

This commit is contained in:
Ignotus Peverell
2017-02-18 18:42:34 -08:00
parent 510feadce6
commit eb024e91d2
16 changed files with 554 additions and 90 deletions
+7 -12
View File
@@ -223,7 +223,7 @@ impl Connection {
pub struct TimeoutConnection {
underlying: Connection,
expected_responses: Arc<Mutex<Vec<(Type, Hash, Option<bool>, Instant)>>>,
expected_responses: Arc<Mutex<Vec<(Type, Option<Hash>, Instant)>>>,
}
impl TimeoutConnection {
@@ -244,15 +244,13 @@ impl TimeoutConnection {
let recv_h = try!(handler.handle(sender, header, data));
let mut expects = exp.lock().unwrap();
println!("EXP1 {}", expects.len());
let filtered = expects.iter()
.filter(|&&(typ, h, _, _)| {
msg_type != typ || recv_h.is_some() && recv_h.unwrap() != h
.filter(|&&(typ, h, _): &&(Type, Option<Hash>, Instant)| {
msg_type != typ || h.is_some() && recv_h != h
})
.map(|&x| x)
.collect::<Vec<_>>();
*expects = filtered;
println!("EXP2 {}", expects.len());
Ok(recv_h)
});
@@ -263,7 +261,7 @@ impl TimeoutConnection {
.interval(Duration::new(2, 0))
.fold((), move |_, _| {
let exp = exp.lock().unwrap();
for &(_, _, _, t) in exp.deref() {
for &(_, _, t) in exp.deref() {
if Instant::now() - t > Duration::new(2, 0) {
return Err(TimerError::TooLong);
}
@@ -283,17 +281,14 @@ impl TimeoutConnection {
/// optionally the hash of the sent data.
pub fn send_request(&self,
t: Type,
rt: Type,
body: &ser::Writeable,
expect_h: Option<(Type, Hash)>)
expect_h: Option<(Hash)>)
-> Result<(), ser::Error> {
let sent = try!(self.underlying.send_msg(t, body));
let mut expects = self.expected_responses.lock().unwrap();
if let Some((rt, h)) = expect_h {
expects.push((rt, h, None, Instant::now()));
} else {
expects.push((t, ZERO_HASH, None, Instant::now()));
}
expects.push((rt, expect_h, Instant::now()));
Ok(())
}
+9 -4
View File
@@ -13,6 +13,7 @@
// limitations under the License.
use std::collections::VecDeque;
use std::net::SocketAddr;
use std::sync::{Arc, RwLock};
use futures::Future;
@@ -47,17 +48,19 @@ impl Handshake {
/// Handles connecting to a new remote peer, starting the version handshake.
pub fn connect(&self,
capab: Capabilities,
total_difficulty: Difficulty,
self_addr: SocketAddr,
conn: TcpStream)
-> Box<Future<Item = (TcpStream, ProtocolV1, PeerInfo), Error = Error>> {
// prepare the first part of the hanshake
let nonce = self.next_nonce();
let hand = Hand {
version: PROTOCOL_VERSION,
capabilities: FULL_HIST,
capabilities: capab,
nonce: nonce,
total_difficulty: total_difficulty,
sender_addr: SockAddr(conn.local_addr().unwrap()),
sender_addr: SockAddr(self_addr),
receiver_addr: SockAddr(conn.peer_addr().unwrap()),
user_agent: USER_AGENT.to_string(),
};
@@ -90,6 +93,7 @@ impl Handshake {
/// Handles receiving a connection from a new remote peer that started the
/// version handshake.
pub fn handshake(&self,
capab: Capabilities,
total_difficulty: Difficulty,
conn: TcpStream)
-> Box<Future<Item = (TcpStream, ProtocolV1, PeerInfo), Error = Error>> {
@@ -116,20 +120,21 @@ impl Handshake {
let peer_info = PeerInfo {
capabilities: hand.capabilities,
user_agent: hand.user_agent,
addr: conn.peer_addr().unwrap(),
addr: hand.sender_addr.0,
version: hand.version,
total_difficulty: hand.total_difficulty,
};
// send our reply with our info
let shake = Shake {
version: PROTOCOL_VERSION,
capabilities: FULL_HIST,
capabilities: capab,
total_difficulty: total_difficulty,
user_agent: USER_AGENT.to_string(),
};
Ok((conn, shake, peer_info))
})
.and_then(|(conn, shake, peer_info)| {
debug!("Success handshake with {}.", peer_info.addr);
write_msg(conn, shake, Type::Shake)
// when more than one protocol version is supported, choosing should go here
.map(|conn| (conn, ProtocolV1::new(), peer_info))
+4 -2
View File
@@ -44,9 +44,11 @@ mod msg;
mod peer;
mod protocol;
mod server;
pub mod store;
mod store;
mod types;
pub use server::{Server, DummyAdapter};
pub use peer::Peer;
pub use types::{P2PConfig, NetAdapter, MAX_LOCATORS, MAX_BLOCK_HEADERS};
pub use types::{P2PConfig, NetAdapter, MAX_LOCATORS, MAX_BLOCK_HEADERS, MAX_PEER_ADDRS,
Capabilities, UNKNOWN, FULL_NODE, FULL_HIST, PeerInfo};
pub use store::{PeerStore, PeerData, State};
+8 -2
View File
@@ -311,10 +311,16 @@ impl Writeable for PeerAddrs {
impl Readable<PeerAddrs> for PeerAddrs {
fn read(reader: &mut Reader) -> Result<PeerAddrs, ser::Error> {
let peer_count = try!(reader.read_u32());
if peer_count > 1000 {
if peer_count > MAX_PEER_ADDRS {
return Err(ser::Error::TooLargeReadErr);
} else if peer_count == 0 {
return Ok(PeerAddrs { peers: vec![] });
}
// let peers = try_map_vec!([0..peer_count], |_| SockAddr::read(reader));
let mut peers = Vec::with_capacity(peer_count as usize);
for _ in 0..peer_count {
peers.push(SockAddr::read(reader)?);
}
let peers = try_map_vec!([0..peer_count], |_| SockAddr::read(reader));
Ok(PeerAddrs { peers: peers })
}
}
+25 -14
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::net::SocketAddr;
use std::sync::Arc;
use futures::Future;
@@ -35,31 +36,36 @@ unsafe impl Send for Peer {}
impl Peer {
/// Initiates the handshake with another peer.
pub fn connect(conn: TcpStream,
capab: Capabilities,
total_difficulty: Difficulty,
self_addr: SocketAddr,
hs: &Handshake)
-> Box<Future<Item = (TcpStream, Peer), Error = Error>> {
let connect_peer = hs.connect(total_difficulty, conn).and_then(|(conn, proto, info)| {
Ok((conn,
Peer {
info: info,
proto: Box::new(proto),
}))
});
let connect_peer = hs.connect(capab, total_difficulty, self_addr, conn)
.and_then(|(conn, proto, info)| {
Ok((conn,
Peer {
info: info,
proto: Box::new(proto),
}))
});
Box::new(connect_peer)
}
/// Accept a handshake initiated by another peer.
pub fn accept(conn: TcpStream,
capab: Capabilities,
total_difficulty: Difficulty,
hs: &Handshake)
-> Box<Future<Item = (TcpStream, Peer), Error = Error>> {
let hs_peer = hs.handshake(total_difficulty, conn).and_then(|(conn, proto, info)| {
Ok((conn,
Peer {
info: info,
proto: Box::new(proto),
}))
});
let hs_peer = hs.handshake(capab, total_difficulty, conn)
.and_then(|(conn, proto, info)| {
Ok((conn,
Peer {
info: info,
proto: Box::new(proto),
}))
});
Box::new(hs_peer)
}
@@ -102,6 +108,11 @@ impl Peer {
self.proto.send_block_request(h)
}
pub fn send_peer_request(&self, capab: Capabilities) -> Result<(), Error> {
debug!("Asking {} for more peers.", self.info.addr);
self.proto.send_peer_request(capab)
}
pub fn stop(&self) {
self.proto.close();
}
+39 -5
View File
@@ -68,7 +68,7 @@ impl Protocol for ProtocolV1 {
/// Sends a ping message to the remote peer. Will panic if handle has never
/// been called on this protocol.
fn send_ping(&self) -> Result<(), ser::Error> {
self.send_request(Type::Ping, &Empty {}, None)
self.send_request(Type::Ping, Type::Pong, &Empty {}, None)
}
/// Serializes and sends a block to our remote peer
@@ -82,11 +82,21 @@ impl Protocol for ProtocolV1 {
}
fn send_header_request(&self, locator: Vec<Hash>) -> Result<(), ser::Error> {
self.send_request(Type::GetHeaders, &Locator { hashes: locator }, None)
self.send_request(Type::GetHeaders,
Type::Headers,
&Locator { hashes: locator },
None)
}
fn send_block_request(&self, h: Hash) -> Result<(), ser::Error> {
self.send_request(Type::GetBlock, &h, Some((Type::Block, h)))
self.send_request(Type::GetBlock, Type::Block, &h, Some(h))
}
fn send_peer_request(&self, capab: Capabilities) -> Result<(), ser::Error> {
self.send_request(Type::GetPeerAddrs,
Type::PeerAddrs,
&GetPeerAddrs { capabilities: capab },
None)
}
/// Close the connection to the remote peer
@@ -102,10 +112,11 @@ impl ProtocolV1 {
fn send_request(&self,
t: Type,
rt: Type,
body: &ser::Writeable,
expect_resp: Option<(Type, Hash)>)
expect_resp: Option<Hash>)
-> Result<(), ser::Error> {
self.conn.borrow().send_request(t, body, expect_resp)
self.conn.borrow().send_request(t, rt, body, expect_resp)
}
}
@@ -168,6 +179,29 @@ fn handle_payload(adapter: &NetAdapter,
adapter.headers_received(headers.headers);
Ok(None)
}
Type::GetPeerAddrs => {
let get_peers = ser::deserialize::<GetPeerAddrs>(&mut &buf[..])?;
let peer_addrs = adapter.find_peer_addrs(get_peers.capabilities);
// serialize and send all the headers over
let mut body_data = vec![];
try!(ser::serialize(&mut body_data,
&PeerAddrs {
peers: peer_addrs.iter().map(|sa| SockAddr(*sa)).collect(),
}));
let mut data = vec![];
try!(ser::serialize(&mut data,
&MsgHeader::new(Type::PeerAddrs, body_data.len() as u64)));
data.append(&mut body_data);
sender.send(data);
Ok(None)
}
Type::PeerAddrs => {
let peer_addrs = ser::deserialize::<PeerAddrs>(&mut &buf[..])?;
adapter.peer_addrs_received(peer_addrs.peers.iter().map(|pa| pa.0).collect());
Ok(None)
}
_ => {
debug!("unknown message type {:?}", header.msg_type);
Ok(None)
+46 -8
View File
@@ -23,7 +23,7 @@ use std::time::Duration;
use futures;
use futures::{Future, Stream};
use futures::future::IntoFuture;
use futures::future::{self, IntoFuture};
use rand::{self, Rng};
use tokio_core::net::{TcpListener, TcpStream};
use tokio_core::reactor;
@@ -51,12 +51,18 @@ impl NetAdapter for DummyAdapter {
fn get_block(&self, h: Hash) -> Option<core::Block> {
None
}
fn find_peer_addrs(&self, capab: Capabilities) -> Vec<SocketAddr> {
vec![]
}
fn peer_addrs_received(&self, peer_addrs: Vec<SocketAddr>) {}
fn peer_connected(&self, pi: &PeerInfo) {}
}
/// P2P server implementation, handling bootstrapping to find and connect to
/// peers, receiving connections from other peers and keep track of all of them.
pub struct Server {
config: P2PConfig,
capabilities: Capabilities,
peers: Arc<RwLock<Vec<Arc<Peer>>>>,
adapter: Arc<NetAdapter>,
stop: RefCell<Option<futures::sync::oneshot::Sender<()>>>,
@@ -68,9 +74,10 @@ unsafe impl Send for Server {}
// TODO TLS
impl Server {
/// Creates a new idle p2p server with no peers
pub fn new(config: P2PConfig, adapter: Arc<NetAdapter>) -> Server {
pub fn new(capab: Capabilities, config: P2PConfig, adapter: Arc<NetAdapter>) -> Server {
Server {
config: config,
capabilities: capab,
peers: Arc::new(RwLock::new(Vec::new())),
adapter: adapter,
stop: RefCell::new(None),
@@ -87,6 +94,7 @@ impl Server {
let hs = Arc::new(Handshake::new());
let peers = self.peers.clone();
let adapter = self.adapter.clone();
let capab = self.capabilities.clone();
// main peer acceptance future handling handshake
let hp = h.clone();
@@ -96,7 +104,9 @@ impl Server {
let peers = peers.clone();
// accept the peer and add it to the server map
let peer_accept = add_to_peers(peers, Peer::accept(conn, total_diff, &hs.clone()));
let peer_accept = add_to_peers(peers,
adapter.clone(),
Peer::accept(conn, capab, total_diff, &hs.clone()));
// wire in a future to timeout the accept after 5 secs
let timed_peer = with_timeout(Box::new(peer_accept), &hp);
@@ -136,23 +146,49 @@ impl Server {
pub fn connect_peer(&self,
addr: SocketAddr,
h: reactor::Handle)
-> Box<Future<Item = (), Error = Error>> {
-> Box<Future<Item = Option<Arc<Peer>>, Error = Error>> {
for p in self.peers.read().unwrap().deref() {
// if we're already connected to the addr, just return the peer
if p.info.addr == addr {
return Box::new(future::ok(Some((*p).clone())));
}
}
// asked to connect to ourselves
if addr.ip() == self.config.host && addr.port() == self.config.port {
return Box::new(future::ok(None));
}
let peers = self.peers.clone();
let adapter1 = self.adapter.clone();
let adapter2 = self.adapter.clone();
let capab = self.capabilities.clone();
let self_addr = SocketAddr::new(self.config.host, self.config.port);
debug!("{} connecting to {}", self_addr, addr);
let socket = TcpStream::connect(&addr, &h).map_err(|e| Error::IOErr(e));
let h2 = h.clone();
let request = socket.and_then(move |socket| {
let peers = peers.clone();
let total_diff = adapter1.total_difficulty();
let total_diff = adapter1.clone().total_difficulty();
// connect to the peer and add it to the server map, wiring it a timeout for
// the handhake
let peer_connect =
add_to_peers(peers, Peer::connect(socket, total_diff, &Handshake::new()));
let peer_connect = add_to_peers(peers,
adapter1,
Peer::connect(socket,
capab,
total_diff,
self_addr,
&Handshake::new()));
with_timeout(Box::new(peer_connect), &h)
})
.and_then(move |(socket, peer)| peer.run(socket, adapter2));
.and_then(move |(socket, peer)| {
h2.spawn(peer.run(socket, adapter2).map_err(|e| {
error!("Peer error: {:?}", e);
()
}));
Ok(Some(peer))
});
Box::new(request)
}
@@ -212,11 +248,13 @@ impl Server {
// Adds the peer built by the provided future in the peers map
fn add_to_peers<A>(peers: Arc<RwLock<Vec<Arc<Peer>>>>,
adapter: Arc<NetAdapter>,
peer_fut: A)
-> Box<Future<Item = Result<(TcpStream, Arc<Peer>), ()>, Error = Error>>
where A: IntoFuture<Item = (TcpStream, Peer), Error = Error> + 'static
{
let peer_add = peer_fut.into_future().map(move |(conn, peer)| {
adapter.peer_connected(&peer.info);
let apeer = Arc::new(peer);
let mut peers = peers.write().unwrap();
peers.push(apeer.clone());
+12 -8
View File
@@ -36,14 +36,14 @@ enum_from_primitive! {
}
}
pub struct Peer {
pub struct PeerData {
pub addr: SocketAddr,
pub capabilities: Capabilities,
pub user_agent: String,
pub flags: State,
}
impl Writeable for Peer {
impl Writeable for PeerData {
fn write(&self, writer: &mut Writer) -> Result<(), ser::Error> {
SockAddr(self.addr).write(writer)?;
ser_multiwrite!(writer,
@@ -54,15 +54,15 @@ impl Writeable for Peer {
}
}
impl Readable<Peer> for Peer {
fn read(reader: &mut Reader) -> Result<Peer, ser::Error> {
impl Readable<PeerData> for PeerData {
fn read(reader: &mut Reader) -> Result<PeerData, ser::Error> {
let addr = SockAddr::read(reader)?;
let (capab, ua, fl) = ser_multiread!(reader, read_u32, read_vec, read_u8);
let user_agent = String::from_utf8(ua).map_err(|_| ser::Error::CorruptedData)?;
let capabilities = Capabilities::from_bits(capab).ok_or(ser::Error::CorruptedData)?;
match State::from_u8(fl) {
Some(flags) => {
Ok(Peer {
Ok(PeerData {
addr: addr.0,
capabilities: capabilities,
user_agent: user_agent,
@@ -84,18 +84,22 @@ impl PeerStore {
Ok(PeerStore { db: db })
}
pub fn save_peer(&self, p: &Peer) -> Result<(), Error> {
pub fn save_peer(&self, p: &PeerData) -> Result<(), Error> {
self.db.put_ser(&to_key(PEER_PREFIX, &mut format!("{}", p.addr).into_bytes())[..],
p)
}
pub fn exists_peer(&self, peer_addr: SocketAddr) -> Result<bool, Error> {
self.db.exists(&to_key(PEER_PREFIX, &mut format!("{}", peer_addr).into_bytes())[..])
}
pub fn delete_peer(&self, peer_addr: SocketAddr) -> Result<(), Error> {
self.db.delete(&to_key(PEER_PREFIX, &mut format!("{}", peer_addr).into_bytes())[..])
}
pub fn find_peers(&self, state: State, cap: Capabilities, count: usize) -> Vec<Peer> {
pub fn find_peers(&self, state: State, cap: Capabilities, count: usize) -> Vec<PeerData> {
let peers_iter = self.db
.iter::<Peer>(&to_key(PEER_PREFIX, &mut "".to_string().into_bytes()));
.iter::<PeerData>(&to_key(PEER_PREFIX, &mut "".to_string().into_bytes()));
let mut peers = Vec::with_capacity(count);
for p in peers_iter {
if p.flags == state && p.capabilities.contains(cap) {
+20
View File
@@ -32,6 +32,9 @@ pub const MAX_BLOCK_HEADERS: u32 = 512;
/// Maximum number of block bodies a peer should ever ask for and send
pub const MAX_BLOCK_BODIES: u32 = 16;
/// Maximum number of peer addresses a peer should ever send
pub const MAX_PEER_ADDRS: u32 = 256;
/// Configuration for the peer-to-peer server.
#[derive(Debug, Clone, Copy)]
pub struct P2PConfig {
@@ -60,6 +63,10 @@ bitflags! {
/// Can provide block headers and the UTXO set for some recent-enough
/// height.
const UTXO_HIST = 0b00000010,
/// Can provide a list of healthy peers
const PEER_LIST = 0b00000100,
const FULL_NODE = FULL_HIST.bits | UTXO_HIST.bits | PEER_LIST.bits,
}
}
@@ -101,6 +108,9 @@ pub trait Protocol {
/// Sends a request for a block from its hash.
fn send_block_request(&self, h: Hash) -> Result<(), Error>;
/// Sends a request for some peer addresses.
fn send_peer_request(&self, capab: Capabilities) -> Result<(), Error>;
/// How many bytes have been sent/received to/from the remote peer.
fn transmitted_bytes(&self) -> (u64, u64);
@@ -133,4 +143,14 @@ pub trait NetAdapter: Sync + Send {
/// Gets a full block by its hash.
fn get_block(&self, h: Hash) -> Option<core::Block>;
/// Find good peers we know with the provided capability and return their
/// addresses.
fn find_peer_addrs(&self, capab: Capabilities) -> Vec<SocketAddr>;
/// A list of peers has been received from one of our peers.
fn peer_addrs_received(&self, Vec<SocketAddr>);
/// Network successfully connected to a peer.
fn peer_connected(&self, &PeerInfo);
}