Remove low-level networking support
- Modify VersionMessage constructor to take in parameters directly that would have otherwise been extracted from a Socket (now removed)
This commit is contained in:
parent
7f11766c65
commit
7e9d393d03
|
@ -1,90 +0,0 @@
|
|||
// Rust Bitcoin Library
|
||||
// Written in 2014 by
|
||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||
//
|
||||
// To the extent possible under law, the author(s) have dedicated all
|
||||
// copyright and related and neighboring rights to this software to
|
||||
// the public domain worldwide. This software is distributed without
|
||||
// any warranty.
|
||||
//
|
||||
// You should have received a copy of the CC0 Public Domain Dedication
|
||||
// along with this software.
|
||||
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
//
|
||||
|
||||
//! Abstract Bitcoin listener
|
||||
//!
|
||||
//! This module defines a listener on the Bitcoin network which is able
|
||||
//! to connect to a peer, send network messages, and receive Bitcoin data.
|
||||
//!
|
||||
|
||||
use std::thread;
|
||||
use std::sync::mpsc::{channel, Receiver};
|
||||
|
||||
use network::constants::Network;
|
||||
use network::message;
|
||||
use network::message::NetworkMessage::Verack;
|
||||
use network::socket::Socket;
|
||||
use util;
|
||||
|
||||
/// A message which can be sent on the Bitcoin network
|
||||
pub trait Listener {
|
||||
/// Return a string encoding of the peer's network address
|
||||
fn peer(&self) -> &str;
|
||||
/// Return the port we have connected to the peer on
|
||||
fn port(&self) -> u16;
|
||||
/// Return the network this `Listener` is operating on
|
||||
fn network(&self) -> Network;
|
||||
/// Main listen loop
|
||||
fn start(&self) -> Result<(Receiver<message::SocketResponse>, Socket), util::Error> {
|
||||
// Open socket
|
||||
let mut ret_sock = Socket::new(self.network());
|
||||
ret_sock.connect(self.peer(), self.port())?;
|
||||
let mut sock = ret_sock.clone();
|
||||
|
||||
let (recv_tx, recv_rx) = channel();
|
||||
|
||||
// Send version message to peer
|
||||
let version_message = sock.version_message(0)?;
|
||||
sock.send_message(version_message)?;
|
||||
|
||||
// Message loop
|
||||
thread::spawn(move || {
|
||||
let mut handshake_complete = false;
|
||||
loop {
|
||||
// Receive new message
|
||||
match sock.receive_message() {
|
||||
Ok(payload) => {
|
||||
// React to any network messages that affect our state.
|
||||
if let Verack = payload {
|
||||
// Make an exception for verack since there is no response required
|
||||
// TODO: when the timeout stuff in std::io::net::tcp is sorted out we should
|
||||
// actually time out if the verack doesn't come in in time
|
||||
if handshake_complete {
|
||||
println!("Received second verack (peer is misbehaving)");
|
||||
} else {
|
||||
handshake_complete = true;
|
||||
}
|
||||
};
|
||||
// We have to pass the message to the main thread for processing,
|
||||
// unfortunately, because sipa says we have to handle everything
|
||||
// in order.
|
||||
recv_tx.send(message::SocketResponse::MessageReceived(payload)).unwrap();
|
||||
}
|
||||
Err(e) => {
|
||||
// On failure we send an error message to the main thread, along with
|
||||
// a channel to receive an acknowledgement that we may tear down this
|
||||
// thread. (If we simply exited immediately, the channel would be torn
|
||||
// down and the main thread would never see the error message.)
|
||||
let (tx, rx) = channel();
|
||||
recv_tx.send(message::SocketResponse::ConnectionFailed(e, tx)).unwrap();
|
||||
rx.recv().unwrap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Ok((recv_rx, ret_sock))
|
||||
}
|
||||
}
|
||||
|
|
@ -18,10 +18,8 @@
|
|||
//! capabilities
|
||||
//!
|
||||
|
||||
use network::constants;
|
||||
use network::address::Address;
|
||||
use network::socket::Socket;
|
||||
use util;
|
||||
use network::constants;
|
||||
|
||||
/// Some simple messages
|
||||
|
||||
|
@ -53,21 +51,26 @@ pub struct VersionMessage {
|
|||
impl VersionMessage {
|
||||
// TODO: we have fixed services and relay to 0
|
||||
/// Constructs a new `version` message
|
||||
pub fn new(timestamp: i64, mut socket: Socket, nonce: u64, start_height: i32) -> Result<VersionMessage, util::Error> {
|
||||
let recv_addr = socket.receiver_address()?;
|
||||
let send_addr = socket.sender_address()?;
|
||||
|
||||
Ok(VersionMessage {
|
||||
pub fn new(
|
||||
services: u64,
|
||||
timestamp: i64,
|
||||
receiver: Address,
|
||||
sender: Address,
|
||||
nonce: u64,
|
||||
user_agent: String,
|
||||
start_height: i32,
|
||||
) -> VersionMessage {
|
||||
VersionMessage {
|
||||
version: constants::PROTOCOL_VERSION,
|
||||
services: socket.services,
|
||||
services: services,
|
||||
timestamp: timestamp,
|
||||
receiver: recv_addr,
|
||||
sender: send_addr,
|
||||
receiver: receiver,
|
||||
sender: sender,
|
||||
nonce: nonce,
|
||||
user_agent: socket.user_agent,
|
||||
user_agent: user_agent,
|
||||
start_height: start_height,
|
||||
relay: false
|
||||
})
|
||||
relay: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -103,6 +106,3 @@ mod tests {
|
|||
assert_eq!(serialize(&real_decode).ok(), Some(from_sat));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -25,11 +25,9 @@ use std::error;
|
|||
pub mod constants;
|
||||
pub mod consensus_params;
|
||||
pub mod encodable;
|
||||
pub mod socket;
|
||||
pub mod serialize;
|
||||
|
||||
pub mod address;
|
||||
pub mod listener;
|
||||
pub mod message;
|
||||
pub mod message_blockdata;
|
||||
pub mod message_network;
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
// Rust Bitcoin Library
|
||||
// Written in 2014 by
|
||||
// Andrew Poelstra <apoelstra@wpsoftware.net>
|
||||
//
|
||||
// To the extent possible under law, the author(s) have dedicated all
|
||||
// copyright and related and neighboring rights to this software to
|
||||
// the public domain worldwide. This software is distributed without
|
||||
// any warranty.
|
||||
//
|
||||
// You should have received a copy of the CC0 Public Domain Dedication
|
||||
// along with this software.
|
||||
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
|
||||
//
|
||||
|
||||
//! Sockets
|
||||
//!
|
||||
//! This module provides support for low-level network communication.
|
||||
//!
|
||||
|
||||
use std::time::{UNIX_EPOCH, SystemTime};
|
||||
use rand::{thread_rng, Rng};
|
||||
use std::io::Write;
|
||||
use std::net;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use network;
|
||||
use network::constants;
|
||||
use network::address::Address;
|
||||
use network::encodable::{ConsensusEncodable, ConsensusDecodable};
|
||||
use network::message::{RawNetworkMessage, NetworkMessage};
|
||||
use network::message::NetworkMessage::Version;
|
||||
use network::message_network::VersionMessage;
|
||||
use network::serialize::{self, RawEncoder, RawDecoder};
|
||||
use util;
|
||||
|
||||
/// Format an IP address in the 16-byte bitcoin protocol serialization
|
||||
fn ipaddr_to_bitcoin_addr(addr: &net::SocketAddr) -> [u16; 8] {
|
||||
match *addr {
|
||||
net::SocketAddr::V4(ref addr) => addr.ip().to_ipv6_mapped().segments(),
|
||||
net::SocketAddr::V6(ref addr) => addr.ip().segments()
|
||||
}
|
||||
}
|
||||
|
||||
/// A network socket along with information about the peer
|
||||
#[derive(Clone)]
|
||||
pub struct Socket {
|
||||
/// The underlying TCP socket
|
||||
socket: Arc<Mutex<Option<net::TcpStream>>>,
|
||||
/// Services supported by us
|
||||
pub services: u64,
|
||||
/// Our user agent
|
||||
pub user_agent: String,
|
||||
/// Nonce to identify our `version` messages
|
||||
pub version_nonce: u64,
|
||||
/// Network magic
|
||||
pub magic: u32
|
||||
}
|
||||
|
||||
macro_rules! with_socket(($s:ident, $sock:ident, $body:block) => ({
|
||||
use ::std::ops::DerefMut;
|
||||
let sock_lock = $s.socket.lock();
|
||||
match sock_lock {
|
||||
Err(_) => {
|
||||
Err(network::Error::SocketMutexPoisoned.into())
|
||||
}
|
||||
Ok(mut guard) => {
|
||||
match *guard.deref_mut() {
|
||||
Some(ref mut $sock) => {
|
||||
$body
|
||||
}
|
||||
None => {
|
||||
Err(network::Error::SocketNotConnectedToPeer.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
impl Socket {
|
||||
// TODO: we fix services to 0
|
||||
/// Construct a new socket
|
||||
pub fn new(network: constants::Network) -> Socket {
|
||||
let mut rng = thread_rng();
|
||||
Socket {
|
||||
socket: Arc::new(Mutex::new(None)),
|
||||
services: 0,
|
||||
version_nonce: rng.gen(),
|
||||
user_agent: constants::USER_AGENT.to_owned(),
|
||||
magic: network.magic(),
|
||||
}
|
||||
}
|
||||
|
||||
/// (Re)connect to the peer
|
||||
pub fn connect(&mut self, host: &str, port: u16) -> Result<(), network::Error> {
|
||||
// Entirely replace the Mutex, in case it was poisoned;
|
||||
// this will also drop any preexisting socket that might be open
|
||||
match net::TcpStream::connect((host, port)) {
|
||||
Ok(s) => {
|
||||
self.socket = Arc::new(Mutex::new(Some(s)));
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
self.socket = Arc::new(Mutex::new(None));
|
||||
Err(network::Error::Io(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Peer address
|
||||
pub fn receiver_address(&mut self) -> Result<Address, network::Error> {
|
||||
with_socket!(self, sock, {
|
||||
match sock.peer_addr() {
|
||||
Ok(addr) => {
|
||||
Ok(Address {
|
||||
services: self.services,
|
||||
address: ipaddr_to_bitcoin_addr(&addr),
|
||||
port: addr.port()
|
||||
})
|
||||
},
|
||||
Err(e) => Err(network::Error::Io(e))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Our own address
|
||||
pub fn sender_address(&mut self) -> Result<Address, network::Error> {
|
||||
with_socket!(self, sock, {
|
||||
match sock.local_addr() {
|
||||
Ok(addr) => {
|
||||
Ok(Address {
|
||||
services: self.services,
|
||||
address: ipaddr_to_bitcoin_addr(&addr),
|
||||
port: addr.port()
|
||||
})
|
||||
},
|
||||
Err(e) => Err(network::Error::Io(e))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Produce a version message appropriate for this socket
|
||||
pub fn version_message(&mut self, start_height: i32) -> Result<NetworkMessage, network::Error> {
|
||||
let recv_addr = self.receiver_address()?;
|
||||
let send_addr = self.sender_address()?;
|
||||
let timestamp = match SystemTime::now().duration_since(UNIX_EPOCH) {
|
||||
Ok(dur) => dur,
|
||||
Err(err) => err.duration(),
|
||||
}.as_secs() as i64;
|
||||
|
||||
Ok(Version(VersionMessage {
|
||||
version: constants::PROTOCOL_VERSION,
|
||||
services: constants::SERVICES,
|
||||
timestamp: timestamp,
|
||||
receiver: recv_addr,
|
||||
sender: send_addr,
|
||||
nonce: self.version_nonce,
|
||||
user_agent: self.user_agent.clone(),
|
||||
start_height: start_height,
|
||||
relay: false
|
||||
}))
|
||||
}
|
||||
|
||||
/// Send a general message across the line
|
||||
pub fn send_message(&mut self, payload: NetworkMessage) -> Result<(), util::Error> {
|
||||
with_socket!(self, sock, {
|
||||
let message = RawNetworkMessage { magic: self.magic, payload: payload };
|
||||
message.consensus_encode(&mut RawEncoder::new(&mut *sock))?;
|
||||
sock.flush().map_err(network::Error::Io).map_err(util::Error::Network)
|
||||
})
|
||||
}
|
||||
|
||||
/// Receive the next message from the peer, decoding the network header
|
||||
/// and verifying its correctness. Returns the undecoded payload.
|
||||
pub fn receive_message(&mut self) -> Result<NetworkMessage, util::Error> {
|
||||
with_socket!(self, sock, {
|
||||
// We need a new scope since the closure in here borrows read_err,
|
||||
// and we try to read it afterward. Letting `iter` go out fixes it.
|
||||
let mut decoder = RawDecoder::new(sock);
|
||||
|
||||
let decoded: RawNetworkMessage = ConsensusDecodable::consensus_decode(&mut decoder)?;
|
||||
|
||||
// Then for magic (this should come before parse error, but we can't
|
||||
// get to it if the deserialization failed). TODO restructure this
|
||||
if decoded.magic != self.magic {
|
||||
Err(serialize::Error::UnexpectedNetworkMagic {
|
||||
expected: self.magic,
|
||||
actual: decoded.magic,
|
||||
}.into())
|
||||
} else {
|
||||
Ok(decoded.payload)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue