Skip to content

Commit

Permalink
Re-implemented the whole interaction between server, peer and protoco…
Browse files Browse the repository at this point in the history
…l to be more Rust-ish. Server maintains peer references and protocol is internally mutable.
  • Loading branch information
ignopeverell committed Oct 29, 2016
1 parent fdaf2ba commit 42769c3
Show file tree
Hide file tree
Showing 9 changed files with 233 additions and 195 deletions.
2 changes: 1 addition & 1 deletion core/src/core/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ impl Block {
// repeated iterations, revisit if a problem

// validate each transaction and gather their proofs
let mut proofs = try_oap_vec!(txs, |tx| tx.verify_sig(&secp));
let mut proofs = try_map_vec!(txs, |tx| tx.verify_sig(&secp));
proofs.push(reward_proof);

// build vectors with all inputs and all outputs, ordering them by hash
Expand Down
2 changes: 1 addition & 1 deletion core/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ macro_rules! map_vec {
/// Same as map_vec when the map closure returns Results. Makes sure the
/// results are "pushed up" and wraps with a try.
#[macro_export]
macro_rules! try_oap_vec {
macro_rules! try_map_vec {
($thing:expr, $mapfn:expr ) => {
try!($thing.iter()
.map($mapfn)
Expand Down
131 changes: 70 additions & 61 deletions p2p/src/handshake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ use std::sync::RwLock;

use rand::Rng;
use rand::os::OsRng;
use mioco::tcp::{TcpStream, Shutdown};

use core::ser::{serialize, deserialize, Error};
use msg::*;
use types::*;
use protocol::ProtocolV1;
use peer::PeerConn;

const NONCES_CAP: usize = 100;

Expand All @@ -44,20 +44,20 @@ impl Handshake {
}

/// Handles connecting to a new remote peer, starting the version handshake.
pub fn connect<'a>(&'a self, peer: &'a mut PeerConn) -> Result<Box<Protocol + 'a>, Error> {
pub fn connect(&self, mut conn: TcpStream) -> Result<(Box<Protocol>, PeerInfo), Error> {
// get a new nonce that can be used on handshake to detect self-connection
let nonce = self.next_nonce();

// send the first part of the handshake
let sender_addr = SockAddr(peer.local_addr());
let receiver_addr = SockAddr(peer.peer_addr());
let opt_err = serialize(peer,
let sender_addr = conn.local_addr().unwrap();
let receiver_addr = conn.peer_addr().unwrap();
let opt_err = serialize(&mut conn,
&Hand {
version: PROTOCOL_VERSION,
capabilities: FULL_SYNC,
nonce: nonce,
sender_addr: sender_addr,
receiver_addr: receiver_addr,
sender_addr: SockAddr(sender_addr),
receiver_addr: SockAddr(receiver_addr),
user_agent: USER_AGENT.to_string(),
});
match opt_err {
Expand All @@ -66,9 +66,9 @@ impl Handshake {
}

// deserialize the handshake response and do version negotiation
let shake = try!(deserialize::<Shake>(peer));
let shake = try!(deserialize::<Shake>(&mut conn));
if shake.version != 1 {
self.close(peer,
self.close(&mut conn,
ErrCodes::UnsupportedVersion as u32,
format!("Unsupported version: {}, ours: {})",
shake.version,
Expand All @@ -78,61 +78,70 @@ impl Handshake {
received: vec![shake.version as u8],
});
}
peer.capabilities = shake.capabilities;
peer.user_agent = shake.user_agent;

info!("Connected to peer {}", peer);
let peer_info = PeerInfo{
capabilities: shake.capabilities,
user_agent: shake.user_agent,
addr: receiver_addr,
version: shake.version,
};

info!("Connected to peer {:?}", peer_info);
// when more than one protocol version is supported, choosing should go here
Ok(Box::new(ProtocolV1::new(peer)))
Ok((Box::new(ProtocolV1::new(conn)), peer_info))
}

/// Handles receiving a connection from a new remote peer that started the
/// version handshake.
pub fn handshake<'a>(&'a self, peer: &'a mut PeerConn) -> Result<Box<Protocol + 'a>, Error> {
// deserialize first part of handshake sent to us and do version negotiation
let hand = try!(deserialize::<Hand>(peer));
if hand.version != 1 {
self.close(peer,
ErrCodes::UnsupportedVersion as u32,
format!("Unsupported version: {}, ours: {})",
hand.version,
PROTOCOL_VERSION));
return Err(Error::UnexpectedData {
expected: vec![PROTOCOL_VERSION as u8],
received: vec![hand.version as u8],
});
}
{
// check the nonce to see if we could be trying to connect to ourselves
let nonces = self.nonces.read().unwrap();
if nonces.contains(&hand.nonce) {
return Err(Error::UnexpectedData {
expected: vec![],
received: vec![],
});
}
}

// all good, keep peer info
peer.capabilities = hand.capabilities;
peer.user_agent = hand.user_agent;

// send our reply with our info
let opt_err = serialize(peer,
&Shake {
version: PROTOCOL_VERSION,
capabilities: FULL_SYNC,
user_agent: USER_AGENT.to_string(),
});
match opt_err {
Some(err) => return Err(err),
None => {}
}

info!("Received connection from peer {}", peer);
// when more than one protocol version is supported, choosing should go here
Ok(Box::new(ProtocolV1::new(peer)))
}
pub fn handshake(&self, mut conn: TcpStream) -> Result<(Box<Protocol>, PeerInfo), Error> {
// deserialize first part of handshake sent to us and do version negotiation
let hand = try!(deserialize::<Hand>(&mut conn));
if hand.version != 1 {
self.close(&mut conn,
ErrCodes::UnsupportedVersion as u32,
format!("Unsupported version: {}, ours: {})",
hand.version,
PROTOCOL_VERSION));
return Err(Error::UnexpectedData {
expected: vec![PROTOCOL_VERSION as u8],
received: vec![hand.version as u8],
});
}
{
// check the nonce to see if we could be trying to connect to ourselves
let nonces = self.nonces.read().unwrap();
if nonces.contains(&hand.nonce) {
return Err(Error::UnexpectedData {
expected: vec![],
received: vec![],
});
}
}

// all good, keep peer info
let peer_info = PeerInfo{
capabilities: hand.capabilities,
user_agent: hand.user_agent,
addr: conn.peer_addr().unwrap(),
version: hand.version,
};

// send our reply with our info
let opt_err = serialize(&mut conn,
&Shake {
version: PROTOCOL_VERSION,
capabilities: FULL_SYNC,
user_agent: USER_AGENT.to_string(),
});
match opt_err {
Some(err) => return Err(err),
None => {}
}

info!("Received connection from peer {:?}", peer_info);
// when more than one protocol version is supported, choosing should go here
Ok((Box::new(ProtocolV1::new(conn)), peer_info))
}

/// Generate a new random nonce and store it in our ring buffer
fn next_nonce(&self) -> u64 {
Expand All @@ -147,12 +156,12 @@ impl Handshake {
nonce
}

fn close(&self, peer: &mut PeerConn, err_code: u32, explanation: String) {
serialize(peer,
fn close(&self, conn: &mut TcpStream, err_code: u32, explanation: String) {
serialize(conn,
&PeerError {
code: err_code,
message: explanation,
});
peer.close();
conn.shutdown(Shutdown::Both);
}
}
1 change: 1 addition & 0 deletions p2p/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extern crate bitflags;
extern crate grin_core as core;
#[macro_use]
extern crate log;
#[macro_use]
extern crate mioco;
extern crate rand;
extern crate time;
Expand Down
66 changes: 55 additions & 11 deletions p2p/src/msg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ use std::net::*;

use core::ser::{self, Writeable, Readable, Writer, Reader, Error};

use types::*;

/// Current latest version of the protocol
pub const PROTOCOL_VERSION: u32 = 1;
/// Grin's user agent with current version (TODO externalize)
Expand All @@ -31,16 +33,6 @@ pub enum ErrCodes {
UnsupportedVersion = 100,
}

bitflags! {
/// Options for block validation
pub flags Capabilities: u32 {
/// We don't know (yet) what the peer can do.
const UNKNOWN = 0b00000000,
/// Runs with the easier version of the Proof of Work, mostly to make testing easier.
const FULL_SYNC = 0b00000001,
}
}

/// Types of messages
#[derive(Clone, Copy)]
pub enum Type {
Expand All @@ -49,6 +41,8 @@ pub enum Type {
SHAKE,
PING,
PONG,
GETPEERADDRS,
PEERADDRS,
/// Never used over the network but to detect unrecognized types.
MaxMsgType,
}
Expand Down Expand Up @@ -92,6 +86,8 @@ impl Readable<MsgHeader> for MsgHeader {
2 => Type::SHAKE,
3 => Type::PING,
4 => Type::PONG,
5 => Type::GETPEERADDRS,
6 => Type::PEERADDRS,
_ => panic!(),
},
})
Expand Down Expand Up @@ -180,6 +176,54 @@ impl Readable<Shake> for Shake {
}
}

/// Ask for other peers addresses, required for network discovery.
pub struct GetPeerAddrs {
/// Filters on the capabilities we'd like the peers to have
pub capabilities: Capabilities,
}

impl Writeable for GetPeerAddrs {
fn write(&self, writer: &mut Writer) -> Option<ser::Error> {
writer.write_u32(self.capabilities.bits())
}
}

impl Readable<GetPeerAddrs> for GetPeerAddrs {
fn read(reader: &mut Reader) -> Result<GetPeerAddrs, ser::Error> {
let capab = try!(reader.read_u32());
let capabilities = try!(Capabilities::from_bits(capab).ok_or(ser::Error::CorruptedData));
Ok(GetPeerAddrs { capabilities: capabilities })
}
}

/// Peer addresses we know of that are fresh enough, in response to
/// GetPeerAddrs.
pub struct PeerAddrs {
pub peers: Vec<SockAddr>,
}

impl Writeable for PeerAddrs {
fn write(&self, writer: &mut Writer) -> Option<ser::Error> {
try_o!(writer.write_u32(self.peers.len() as u32));
for p in &self.peers {
p.write(writer);
}
None
}
}

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 {
return Err(ser::Error::TooLargeReadErr(format!("Too many peers provided: {}",
peer_count)));
}
let peers = try_map_vec!([0..peer_count], |_| SockAddr::read(reader));
Ok(PeerAddrs { peers: peers })
}
}

/// We found some issue in the communication, sending an error back, usually
/// followed by closing the connection.
pub struct PeerError {
Expand Down Expand Up @@ -247,7 +291,7 @@ impl Readable<SockAddr> for SockAddr {
ip[3]),
port))))
} else {
let ip = try_oap_vec!([0..8], |_| reader.read_u16());
let ip = try_map_vec!([0..8], |_| reader.read_u16());
let port = try!(reader.read_u16());
Ok(SockAddr(SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(ip[0],
ip[1],
Expand Down
Loading

0 comments on commit 42769c3

Please sign in to comment.