2014-07-18 13:56:17 +00:00
|
|
|
// 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/>.
|
|
|
|
//
|
|
|
|
|
|
|
|
//! # Network message
|
|
|
|
//!
|
|
|
|
//! This module defines the `Message` traits which are used
|
|
|
|
//! for (de)serializing Bitcoin objects for transmission on the network. It
|
|
|
|
//! also defines (de)serialization routines for many primitives.
|
|
|
|
//!
|
|
|
|
|
|
|
|
use collections::Vec;
|
2015-04-05 19:43:44 +00:00
|
|
|
use std::iter;
|
2015-04-04 17:56:40 +00:00
|
|
|
use std::io::{self, Cursor};
|
2015-04-05 19:43:44 +00:00
|
|
|
use std::sync::mpsc::Sender;
|
2014-07-18 13:56:17 +00:00
|
|
|
|
|
|
|
use blockdata::block;
|
2014-08-23 23:37:01 +00:00
|
|
|
use blockdata::transaction;
|
2014-07-18 13:56:17 +00:00
|
|
|
use network::address::Address;
|
|
|
|
use network::message_network;
|
|
|
|
use network::message_blockdata;
|
2014-08-01 16:01:39 +00:00
|
|
|
use network::encodable::{ConsensusDecodable, ConsensusEncodable};
|
|
|
|
use network::encodable::CheckedData;
|
|
|
|
use network::serialize::{serialize, RawDecoder, SimpleEncoder, SimpleDecoder};
|
2014-07-18 13:56:17 +00:00
|
|
|
use util::misc::prepend_err;
|
|
|
|
|
|
|
|
/// Serializer for command string
|
2015-03-26 15:35:31 +00:00
|
|
|
#[derive(PartialEq, Eq, Clone, Debug)]
|
2014-07-18 13:56:17 +00:00
|
|
|
pub struct CommandString(pub String);
|
|
|
|
|
2015-04-06 00:10:37 +00:00
|
|
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for CommandString {
|
2014-08-01 16:01:39 +00:00
|
|
|
#[inline]
|
2015-04-06 00:10:37 +00:00
|
|
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
2014-07-18 13:56:17 +00:00
|
|
|
let &CommandString(ref inner_str) = self;
|
2015-01-18 18:16:01 +00:00
|
|
|
let mut rawbytes = [0u8; 12];
|
2014-08-14 22:20:39 +00:00
|
|
|
rawbytes.clone_from_slice(inner_str.as_bytes().as_slice());
|
2014-08-01 16:01:39 +00:00
|
|
|
rawbytes.consensus_encode(s)
|
2014-07-18 13:56:17 +00:00
|
|
|
}
|
2014-08-01 16:01:39 +00:00
|
|
|
}
|
2014-07-18 13:56:17 +00:00
|
|
|
|
2015-04-06 00:10:37 +00:00
|
|
|
impl<D: SimpleDecoder> ConsensusDecodable<D> for CommandString {
|
2014-08-01 16:01:39 +00:00
|
|
|
#[inline]
|
2015-04-06 00:10:37 +00:00
|
|
|
fn consensus_decode(d: &mut D) -> Result<CommandString, D::Error> {
|
2015-01-18 18:16:01 +00:00
|
|
|
let rawbytes: [u8; 12] = try!(ConsensusDecodable::consensus_decode(d));
|
2015-04-05 19:43:44 +00:00
|
|
|
let rv = iter::FromIterator::from_iter(rawbytes.iter().filter_map(|&u| if u > 0 { Some(u as char) } else { None }));
|
2014-08-01 16:01:39 +00:00
|
|
|
Ok(CommandString(rv))
|
2014-07-18 13:56:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A Network message
|
|
|
|
pub struct RawNetworkMessage {
|
|
|
|
/// Magic bytes to identify the network these messages are meant for
|
|
|
|
pub magic: u32,
|
|
|
|
/// The actual message data
|
|
|
|
pub payload: NetworkMessage
|
|
|
|
}
|
|
|
|
|
2014-09-10 12:15:48 +00:00
|
|
|
/// A response from the peer-connected socket
|
|
|
|
pub enum SocketResponse {
|
|
|
|
/// A message was received
|
|
|
|
MessageReceived(NetworkMessage),
|
|
|
|
/// An error occured and the socket needs to close
|
2015-03-26 16:52:20 +00:00
|
|
|
ConnectionFailed(io::Error, Sender<()>)
|
2014-09-10 12:15:48 +00:00
|
|
|
}
|
|
|
|
|
2015-03-26 15:35:31 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
2014-07-18 13:56:17 +00:00
|
|
|
/// A Network message payload. Proper documentation is available on the Bitcoin
|
|
|
|
/// wiki https://en.bitcoin.it/wiki/Protocol_specification
|
2014-09-10 12:15:48 +00:00
|
|
|
pub enum NetworkMessage {
|
2014-07-18 13:56:17 +00:00
|
|
|
/// `version`
|
|
|
|
Version(message_network::VersionMessage),
|
|
|
|
/// `verack`
|
|
|
|
Verack,
|
|
|
|
/// `addr`
|
|
|
|
Addr(Vec<(u32, Address)>),
|
|
|
|
/// `inv`
|
|
|
|
Inv(Vec<message_blockdata::Inventory>),
|
|
|
|
/// `getdata`
|
|
|
|
GetData(Vec<message_blockdata::Inventory>),
|
|
|
|
/// `notfound`
|
|
|
|
NotFound(Vec<message_blockdata::Inventory>),
|
|
|
|
/// `getblocks`
|
|
|
|
GetBlocks(message_blockdata::GetBlocksMessage),
|
|
|
|
/// `getheaders`
|
|
|
|
GetHeaders(message_blockdata::GetHeadersMessage),
|
2014-08-23 23:37:01 +00:00
|
|
|
/// tx
|
|
|
|
Tx(transaction::Transaction),
|
2014-07-18 13:56:17 +00:00
|
|
|
/// `block`
|
|
|
|
Block(block::Block),
|
|
|
|
/// `headers`
|
|
|
|
Headers(Vec<block::LoneBlockHeader>),
|
|
|
|
// TODO: getaddr,
|
|
|
|
// TODO: mempool,
|
|
|
|
// TODO: checkorder,
|
|
|
|
// TODO: submitorder,
|
|
|
|
// TODO: reply,
|
|
|
|
/// `ping`
|
|
|
|
Ping(u64),
|
|
|
|
/// `pong`
|
2014-09-10 12:15:48 +00:00
|
|
|
Pong(u64)
|
2014-07-18 13:56:17 +00:00
|
|
|
// TODO: reject,
|
|
|
|
// TODO: bloom filtering
|
|
|
|
// TODO: alert
|
|
|
|
}
|
|
|
|
|
|
|
|
impl RawNetworkMessage {
|
|
|
|
fn command(&self) -> String {
|
|
|
|
match self.payload {
|
2015-04-05 17:58:49 +00:00
|
|
|
NetworkMessage::Version(_) => "version",
|
|
|
|
NetworkMessage::Verack => "verack",
|
|
|
|
NetworkMessage::Addr(_) => "addr",
|
|
|
|
NetworkMessage::Inv(_) => "inv",
|
|
|
|
NetworkMessage::GetData(_) => "getdata",
|
|
|
|
NetworkMessage::NotFound(_) => "notfound",
|
|
|
|
NetworkMessage::GetBlocks(_) => "getblocks",
|
|
|
|
NetworkMessage::GetHeaders(_) => "getheaders",
|
|
|
|
NetworkMessage::Tx(_) => "tx",
|
|
|
|
NetworkMessage::Block(_) => "block",
|
|
|
|
NetworkMessage::Headers(_) => "headers",
|
|
|
|
NetworkMessage::Ping(_) => "ping",
|
|
|
|
NetworkMessage::Pong(_) => "pong",
|
2014-07-18 13:56:17 +00:00
|
|
|
}.to_string()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-06 00:10:37 +00:00
|
|
|
impl<S: SimpleEncoder> ConsensusEncodable<S> for RawNetworkMessage {
|
|
|
|
fn consensus_encode(&self, s: &mut S) -> Result<(), S::Error> {
|
2014-08-01 16:01:39 +00:00
|
|
|
try!(self.magic.consensus_encode(s));
|
|
|
|
try!(CommandString(self.command()).consensus_encode(s));
|
2014-08-03 01:22:27 +00:00
|
|
|
try!(CheckedData(match self.payload {
|
2015-04-05 17:58:49 +00:00
|
|
|
NetworkMessage::Version(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::Verack => Ok(vec![]),
|
|
|
|
NetworkMessage::Addr(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::Inv(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::GetData(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::NotFound(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::GetBlocks(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::GetHeaders(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::Tx(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::Block(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::Headers(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::Ping(ref dat) => serialize(dat),
|
|
|
|
NetworkMessage::Pong(ref dat) => serialize(dat),
|
2014-08-03 01:22:27 +00:00
|
|
|
}.unwrap()).consensus_encode(s));
|
2014-08-01 16:01:39 +00:00
|
|
|
Ok(())
|
2014-07-18 13:56:17 +00:00
|
|
|
}
|
2014-08-01 16:01:39 +00:00
|
|
|
}
|
|
|
|
|
2015-04-06 00:10:37 +00:00
|
|
|
impl<D: SimpleDecoder<Error=io::Error>> ConsensusDecodable<D> for RawNetworkMessage {
|
2015-03-26 16:52:20 +00:00
|
|
|
fn consensus_decode(d: &mut D) -> io::Result<RawNetworkMessage> {
|
2014-08-01 16:01:39 +00:00
|
|
|
let magic = try!(ConsensusDecodable::consensus_decode(d));
|
|
|
|
let CommandString(cmd): CommandString= try!(ConsensusDecodable::consensus_decode(d));
|
|
|
|
let CheckedData(raw_payload): CheckedData = try!(ConsensusDecodable::consensus_decode(d));
|
2014-07-18 13:56:17 +00:00
|
|
|
|
2015-04-04 17:56:40 +00:00
|
|
|
let mut mem_d = RawDecoder::new(Cursor::new(raw_payload));
|
2014-07-18 13:56:17 +00:00
|
|
|
let payload = match cmd.as_slice() {
|
2015-04-05 17:58:49 +00:00
|
|
|
"version" => NetworkMessage::Version(try!(prepend_err("version", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"verack" => NetworkMessage::Verack,
|
|
|
|
"addr" => NetworkMessage::Addr(try!(prepend_err("addr", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"inv" => NetworkMessage::Inv(try!(prepend_err("inv", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"getdata" => NetworkMessage::GetData(try!(prepend_err("getdata", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"notfound" => NetworkMessage::NotFound(try!(prepend_err("notfound", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"getblocks" => NetworkMessage::GetBlocks(try!(prepend_err("getblocks", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"getheaders" => NetworkMessage::GetHeaders(try!(prepend_err("getheaders", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"block" => NetworkMessage::Block(try!(prepend_err("block", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"headers" => NetworkMessage::Headers(try!(prepend_err("headers", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"ping" => NetworkMessage::Ping(try!(prepend_err("ping", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"pong" => NetworkMessage::Ping(try!(prepend_err("pong", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
|
|
|
"tx" => NetworkMessage::Tx(try!(prepend_err("tx", ConsensusDecodable::consensus_decode(&mut mem_d)))),
|
2014-07-18 13:56:17 +00:00
|
|
|
cmd => {
|
2015-03-26 16:52:20 +00:00
|
|
|
return Err(io::Error {
|
|
|
|
kind: io::ErrorKind::OtherError,
|
2014-07-18 13:56:17 +00:00
|
|
|
desc: "unknown message type",
|
|
|
|
detail: Some(format!("`{}` not recognized", cmd))
|
|
|
|
});
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(RawNetworkMessage {
|
|
|
|
magic: magic,
|
|
|
|
payload: payload
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2014-08-03 01:22:27 +00:00
|
|
|
use super::{RawNetworkMessage, CommandString, Verack, Ping};
|
2014-08-01 16:01:39 +00:00
|
|
|
|
2015-03-26 16:52:20 +00:00
|
|
|
use std::io::io::Result;
|
2014-07-18 13:56:17 +00:00
|
|
|
|
2014-08-01 16:01:39 +00:00
|
|
|
use network::serialize::{deserialize, serialize};
|
2014-07-18 13:56:17 +00:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn serialize_commandstring_test() {
|
|
|
|
let cs = CommandString(String::from_str("Andrew"));
|
2014-08-01 16:01:39 +00:00
|
|
|
assert_eq!(serialize(&cs), Ok(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]));
|
2014-07-18 13:56:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn deserialize_commandstring_test() {
|
2015-03-26 16:52:20 +00:00
|
|
|
let cs: io::Result<CommandString> = deserialize(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]);
|
2014-07-18 13:56:17 +00:00
|
|
|
assert!(cs.is_ok());
|
|
|
|
assert_eq!(cs.unwrap(), CommandString(String::from_str("Andrew")));
|
|
|
|
|
2015-03-26 16:52:20 +00:00
|
|
|
let short_cs: io::Result<CommandString> = deserialize(vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]);
|
2014-07-18 13:56:17 +00:00
|
|
|
assert!(short_cs.is_err());
|
|
|
|
}
|
|
|
|
|
2014-08-03 01:22:27 +00:00
|
|
|
#[test]
|
|
|
|
fn serialize_verack_test() {
|
|
|
|
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: Verack }),
|
|
|
|
Ok(vec![0xf9, 0xbe, 0xb4, 0xd9, 0x76, 0x65, 0x72, 0x61,
|
|
|
|
0x63, 0x6B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn serialize_ping_test() {
|
|
|
|
assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: Ping(100) }),
|
|
|
|
Ok(vec![0xf9, 0xbe, 0xb4, 0xd9, 0x70, 0x69, 0x6e, 0x67,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x08, 0x00, 0x00, 0x00, 0x24, 0x67, 0xf1, 0x1d,
|
|
|
|
0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]));
|
|
|
|
}
|
2014-07-18 13:56:17 +00:00
|
|
|
}
|
|
|
|
|