// Rust Bitcoin Library // Written in 2014 by // Andrew Poelstra // // 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 . // //! 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 std::iter; use std::io::Cursor; use std::sync::mpsc::Sender; use blockdata::block; use blockdata::transaction; use network::address::Address; use network::message_network; use network::message_blockdata; use consensus::encode::{Decodable, Encodable}; use consensus::encode::CheckedData; use consensus::encode::{self, serialize, Encoder, Decoder}; use util; /// Serializer for command string #[derive(PartialEq, Eq, Clone, Debug)] pub struct CommandString(pub String); impl Encodable for CommandString { #[inline] fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> { let &CommandString(ref inner_str) = self; let mut rawbytes = [0u8; 12]; let strbytes = inner_str.as_bytes(); if strbytes.len() > 12 { panic!("Command string longer than 12 bytes"); } for x in 0..strbytes.len() { rawbytes[x] = strbytes[x]; } rawbytes.consensus_encode(s) } } impl Decodable for CommandString { #[inline] fn consensus_decode(d: &mut D) -> Result { let rawbytes: [u8; 12] = Decodable::consensus_decode(d)?; let rv = iter::FromIterator::from_iter(rawbytes.iter().filter_map(|&u| if u > 0 { Some(u as char) } else { None })); Ok(CommandString(rv)) } } /// 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 } /// A response from the peer-connected socket pub enum SocketResponse { /// A message was received MessageReceived(NetworkMessage), /// An error occurred and the socket needs to close ConnectionFailed(util::Error, Sender<()>) } #[derive(Clone, PartialEq, Eq, Debug)] /// A Network message payload. Proper documentation is available on at /// [Bitcoin Wiki: Protocol Specification](https://en.bitcoin.it/wiki/Protocol_specification) pub enum NetworkMessage { /// `version` Version(message_network::VersionMessage), /// `verack` Verack, /// `addr` Addr(Vec<(u32, Address)>), /// `inv` Inv(Vec), /// `getdata` GetData(Vec), /// `notfound` NotFound(Vec), /// `getblocks` GetBlocks(message_blockdata::GetBlocksMessage), /// `getheaders` GetHeaders(message_blockdata::GetHeadersMessage), /// `mempool` MemPool, /// tx Tx(transaction::Transaction), /// `block` Block(block::Block), /// `headers` Headers(Vec), /// `getaddr` GetAddr, // TODO: checkorder, // TODO: submitorder, // TODO: reply, /// `ping` Ping(u64), /// `pong` Pong(u64), // TODO: reject, // TODO: bloom filtering // TODO: alert /// `alert` Alert(Vec) } impl RawNetworkMessage { /// Return the message command. This is useful for debug outputs. pub fn command(&self) -> String { match self.payload { 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::MemPool => "mempool", NetworkMessage::Tx(_) => "tx", NetworkMessage::Block(_) => "block", NetworkMessage::Headers(_) => "headers", NetworkMessage::GetAddr => "getaddr", NetworkMessage::Ping(_) => "ping", NetworkMessage::Pong(_) => "pong", NetworkMessage::Alert(_) => "alert", }.to_owned() } } impl Encodable for RawNetworkMessage { fn consensus_encode(&self, s: &mut S) -> Result<(), encode::Error> { self.magic.consensus_encode(s)?; CommandString(self.command()).consensus_encode(s)?; CheckedData(match self.payload { NetworkMessage::Version(ref dat) => serialize(dat), 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), NetworkMessage::Alert(ref dat) => serialize(dat), NetworkMessage::Verack | NetworkMessage::MemPool | NetworkMessage::GetAddr => vec![], }).consensus_encode(s) } } impl Decodable for RawNetworkMessage { fn consensus_decode(d: &mut D) -> Result { let magic = Decodable::consensus_decode(d)?; let CommandString(cmd): CommandString= Decodable::consensus_decode(d)?; let CheckedData(raw_payload): CheckedData = Decodable::consensus_decode(d)?; let mut mem_d = Cursor::new(raw_payload); let payload = match &cmd[..] { "version" => NetworkMessage::Version(Decodable::consensus_decode(&mut mem_d)?), "verack" => NetworkMessage::Verack, "addr" => NetworkMessage::Addr(Decodable::consensus_decode(&mut mem_d)?), "inv" => NetworkMessage::Inv(Decodable::consensus_decode(&mut mem_d)?), "getdata" => NetworkMessage::GetData(Decodable::consensus_decode(&mut mem_d)?), "notfound" => NetworkMessage::NotFound(Decodable::consensus_decode(&mut mem_d)?), "getblocks" => NetworkMessage::GetBlocks(Decodable::consensus_decode(&mut mem_d)?), "getheaders" => NetworkMessage::GetHeaders(Decodable::consensus_decode(&mut mem_d)?), "mempool" => NetworkMessage::MemPool, "block" => NetworkMessage::Block(Decodable::consensus_decode(&mut mem_d)?), "headers" => NetworkMessage::Headers(Decodable::consensus_decode(&mut mem_d)?), "getaddr" => NetworkMessage::GetAddr, "ping" => NetworkMessage::Ping(Decodable::consensus_decode(&mut mem_d)?), "pong" => NetworkMessage::Pong(Decodable::consensus_decode(&mut mem_d)?), "tx" => NetworkMessage::Tx(Decodable::consensus_decode(&mut mem_d)?), "alert" => NetworkMessage::Alert(Decodable::consensus_decode(&mut mem_d)?), _ => return Err(encode::Error::UnrecognizedNetworkCommand(cmd)), }; Ok(RawNetworkMessage { magic: magic, payload: payload }) } } #[cfg(test)] mod test { use super::{RawNetworkMessage, NetworkMessage, CommandString}; use consensus::encode::{deserialize, serialize}; #[test] fn serialize_commandstring_test() { let cs = CommandString("Andrew".to_owned()); assert_eq!(serialize(&cs), vec![0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]); } #[test] fn deserialize_commandstring_test() { let cs: Result = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0, 0]); assert!(cs.is_ok()); assert_eq!(cs.unwrap(), CommandString("Andrew".to_owned())); let short_cs: Result = deserialize(&[0x41u8, 0x6e, 0x64, 0x72, 0x65, 0x77, 0, 0, 0, 0, 0]); assert!(short_cs.is_err()); } #[test] fn serialize_verack_test() { assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::Verack }), 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: NetworkMessage::Ping(100) }), 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]); } #[test] fn serialize_mempool_test() { assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::MemPool }), vec![0xf9, 0xbe, 0xb4, 0xd9, 0x6d, 0x65, 0x6d, 0x70, 0x6f, 0x6f, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]); } #[test] fn serialize_getaddr_test() { assert_eq!(serialize(&RawNetworkMessage { magic: 0xd9b4bef9, payload: NetworkMessage::GetAddr }), vec![0xf9, 0xbe, 0xb4, 0xd9, 0x67, 0x65, 0x74, 0x61, 0x64, 0x64, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0xf6, 0xe0, 0xe2]); } }