Replace `Version` `MetaAddr` with `(Services, SocketAddr)`. (#12)
* Replace Version MetaAddr by (Services, SocketAddr). The version handshake message doesn't include last-seen timestamps for the address fields, unlike other messages, so instead of modeling the message data with a `MetaAddr` (which includes a timestamp), we should just use a tuple. * Simplify try_read_version implementation. Because we no longer need to construct fake timestamps for the `MetaAddr` fields, we don't need to use any of the parsed fields while parsing later fields, and we can neatly wrap up the entire parsing logic into a single expression. * fmt I didn't have the toolchain-specified `rustfmt` because I was mostly offline and couldn't download it.
This commit is contained in:
parent
73740841e1
commit
f45bbeba98
|
@ -5,7 +5,7 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate failure;
|
extern crate failure;
|
||||||
|
|
||||||
pub mod message;
|
|
||||||
pub mod types;
|
|
||||||
mod constants;
|
mod constants;
|
||||||
|
pub mod message;
|
||||||
mod meta_addr;
|
mod meta_addr;
|
||||||
|
pub mod types;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
//! Definitions of network messages.
|
//! Definitions of network messages.
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
|
use std::net;
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use chrono::{DateTime, TimeZone, Utc};
|
use chrono::{DateTime, TimeZone, Utc};
|
||||||
|
@ -57,17 +58,15 @@ pub enum Message {
|
||||||
/// The time when the version message was sent.
|
/// The time when the version message was sent.
|
||||||
timestamp: DateTime<Utc>,
|
timestamp: DateTime<Utc>,
|
||||||
|
|
||||||
/// The network address of the node receiving this message.
|
/// The network address of the node receiving this message, and its
|
||||||
|
/// advertised network services.
|
||||||
///
|
///
|
||||||
/// Note that the timestamp field of the [`MetaAddr`] is not included in
|
/// Q: how does the handshake know the remote peer's services already?
|
||||||
/// the serialization of `version` messages.
|
address_recv: (Services, net::SocketAddr),
|
||||||
address_receiving: MetaAddr,
|
|
||||||
|
|
||||||
/// The network address of the node emitting this message.
|
/// The network address of the node sending this message, and its
|
||||||
///
|
/// advertised network services.
|
||||||
/// Note that the timestamp field of the [`MetaAddr`] is not included in
|
address_from: (Services, net::SocketAddr),
|
||||||
/// the serialization of `version` messages.
|
|
||||||
address_from: MetaAddr,
|
|
||||||
|
|
||||||
/// Node random nonce, randomly generated every time a version
|
/// Node random nonce, randomly generated every time a version
|
||||||
/// packet is sent. This nonce is used to detect connections
|
/// packet is sent. This nonce is used to detect connections
|
||||||
|
@ -353,7 +352,7 @@ impl Message {
|
||||||
ref version,
|
ref version,
|
||||||
ref services,
|
ref services,
|
||||||
ref timestamp,
|
ref timestamp,
|
||||||
ref address_receiving,
|
ref address_recv,
|
||||||
ref address_from,
|
ref address_from,
|
||||||
ref nonce,
|
ref nonce,
|
||||||
ref user_agent,
|
ref user_agent,
|
||||||
|
@ -364,14 +363,13 @@ impl Message {
|
||||||
writer.write_u64::<LittleEndian>(services.0)?;
|
writer.write_u64::<LittleEndian>(services.0)?;
|
||||||
writer.write_i64::<LittleEndian>(timestamp.timestamp())?;
|
writer.write_i64::<LittleEndian>(timestamp.timestamp())?;
|
||||||
|
|
||||||
// We represent a Bitcoin net_addr as a `MetaAddr` internally.
|
let (recv_services, recv_addr) = address_recv;
|
||||||
// However, the version message encodes net_addrs without the
|
writer.write_u64::<LittleEndian>(recv_services.0)?;
|
||||||
// timestamp field, so we encode the `MetaAddr`s manually here.
|
writer.write_socket_addr(*recv_addr)?;
|
||||||
writer.write_u64::<LittleEndian>(address_receiving.services.0)?;
|
|
||||||
writer.write_socket_addr(address_receiving.addr)?;
|
|
||||||
|
|
||||||
writer.write_u64::<LittleEndian>(address_from.services.0)?;
|
let (from_services, from_addr) = address_from;
|
||||||
writer.write_socket_addr(address_from.addr)?;
|
writer.write_u64::<LittleEndian>(from_services.0)?;
|
||||||
|
writer.write_socket_addr(*from_addr)?;
|
||||||
|
|
||||||
writer.write_u64::<LittleEndian>(nonce.0)?;
|
writer.write_u64::<LittleEndian>(nonce.0)?;
|
||||||
writer.write_string(&user_agent)?;
|
writer.write_string(&user_agent)?;
|
||||||
|
@ -459,44 +457,26 @@ fn try_read_version<R: io::Read>(
|
||||||
mut reader: R,
|
mut reader: R,
|
||||||
_parsing_version: Version,
|
_parsing_version: Version,
|
||||||
) -> Result<Message, SerializationError> {
|
) -> Result<Message, SerializationError> {
|
||||||
let version = Version(reader.read_u32::<LittleEndian>()?);
|
Ok(Message::Version {
|
||||||
let services = Services(reader.read_u64::<LittleEndian>()?);
|
version: Version(reader.read_u32::<LittleEndian>()?),
|
||||||
let timestamp = Utc.timestamp(reader.read_i64::<LittleEndian>()?, 0);
|
|
||||||
|
|
||||||
// We represent a Bitcoin `net_addr` as a `MetaAddr` internally. However,
|
|
||||||
// the version message encodes `net_addr`s without timestamps, so we fill in
|
|
||||||
// the timestamp field of the `MetaAddr`s with the version message's
|
|
||||||
// timestamp.
|
|
||||||
let address_receiving = MetaAddr {
|
|
||||||
services: Services(reader.read_u64::<LittleEndian>()?),
|
services: Services(reader.read_u64::<LittleEndian>()?),
|
||||||
addr: reader.read_socket_addr()?,
|
timestamp: Utc.timestamp(reader.read_i64::<LittleEndian>()?, 0),
|
||||||
last_seen: timestamp,
|
address_recv: (
|
||||||
};
|
Services(reader.read_u64::<LittleEndian>()?),
|
||||||
let address_from = MetaAddr {
|
reader.read_socket_addr()?,
|
||||||
services: Services(reader.read_u64::<LittleEndian>()?),
|
),
|
||||||
addr: reader.read_socket_addr()?,
|
address_from: (
|
||||||
last_seen: timestamp,
|
Services(reader.read_u64::<LittleEndian>()?),
|
||||||
};
|
reader.read_socket_addr()?,
|
||||||
|
),
|
||||||
let nonce = Nonce(reader.read_u64::<LittleEndian>()?);
|
nonce: Nonce(reader.read_u64::<LittleEndian>()?),
|
||||||
let user_agent = reader.read_string()?;
|
user_agent: reader.read_string()?,
|
||||||
let start_height = BlockHeight(reader.read_u32::<LittleEndian>()?);
|
start_height: BlockHeight(reader.read_u32::<LittleEndian>()?),
|
||||||
let relay = match reader.read_u8()? {
|
relay: match reader.read_u8()? {
|
||||||
0 => false,
|
0 => false,
|
||||||
1 => true,
|
1 => true,
|
||||||
_ => return Err(SerializationError::ParseError("non-bool value")),
|
_ => return Err(SerializationError::ParseError("non-bool value")),
|
||||||
};
|
},
|
||||||
|
|
||||||
Ok(Message::Version {
|
|
||||||
version,
|
|
||||||
services,
|
|
||||||
timestamp,
|
|
||||||
address_receiving,
|
|
||||||
address_from,
|
|
||||||
nonce,
|
|
||||||
user_agent,
|
|
||||||
start_height,
|
|
||||||
relay,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,16 +628,14 @@ mod tests {
|
||||||
services,
|
services,
|
||||||
timestamp,
|
timestamp,
|
||||||
// XXX maybe better to have Version keep only (Services, SocketAddr)
|
// XXX maybe better to have Version keep only (Services, SocketAddr)
|
||||||
address_receiving: MetaAddr {
|
address_recv: (
|
||||||
services,
|
services,
|
||||||
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)), 8233),
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)), 8233),
|
||||||
last_seen: timestamp,
|
),
|
||||||
},
|
address_from: (
|
||||||
address_from: MetaAddr {
|
|
||||||
services,
|
services,
|
||||||
addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)), 8233),
|
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(203, 0, 113, 6)), 8233),
|
||||||
last_seen: timestamp,
|
),
|
||||||
},
|
|
||||||
nonce: Nonce(0x9082_4908_8927_9238),
|
nonce: Nonce(0x9082_4908_8927_9238),
|
||||||
user_agent: "Zebra".to_owned(),
|
user_agent: "Zebra".to_owned(),
|
||||||
start_height: BlockHeight(540_000),
|
start_height: BlockHeight(540_000),
|
||||||
|
|
Loading…
Reference in New Issue