use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use messages::message::Payload;
use messages::node_addr::NodeAddr;
use std::io;
use std::io::{Read, Write};
use std::time::UNIX_EPOCH;
use util::{secs_since, var_int, Error, Result, Serializable};
pub const PROTOCOL_VERSION: u32 = 70015;
pub const MIN_SUPPORTED_PROTOCOL_VERSION: u32 = 70001;
pub const UNKNOWN_IP: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1];
pub const NODE_NONE: u64 = 0;
pub const NODE_NETWORK: u64 = 1;
pub const NODE_BITCOIN_CASH: u64 = 1 << 5;
#[derive(Debug, Default, PartialEq, Eq, Hash, Clone)]
pub struct Version {
pub version: u32,
pub services: u64,
pub timestamp: i64,
pub recv_addr: NodeAddr,
pub tx_addr: NodeAddr,
pub nonce: u64,
pub user_agent: String,
pub start_height: i32,
pub relay: bool,
}
impl Version {
pub fn validate(&self) -> Result<()> {
if self.version < MIN_SUPPORTED_PROTOCOL_VERSION {
let msg = format!("Unsupported protocol version: {}", self.version);
return Err(Error::BadData(msg));
}
let now = secs_since(UNIX_EPOCH) as i64;
if (self.timestamp - now).abs() > 2 * 60 * 60 {
let msg = format!("Timestamp too old: {}", self.timestamp);
return Err(Error::BadData(msg));
}
Ok(())
}
}
impl Serializable<Version> for Version {
fn read(reader: &mut dyn Read) -> Result<Version> {
let mut ret = Version {
..Default::default()
};
ret.version = reader.read_u32::<LittleEndian>()?;
ret.services = reader.read_u64::<LittleEndian>()?;
ret.timestamp = reader.read_i64::<LittleEndian>()?;
ret.recv_addr = NodeAddr::read(reader)?;
ret.tx_addr = NodeAddr::read(reader)?;
ret.nonce = reader.read_u64::<LittleEndian>()?;
let user_agent_size = var_int::read(reader)? as usize;
let mut user_agent_bytes = vec![0; user_agent_size];
reader.read(&mut user_agent_bytes)?;
ret.user_agent = String::from_utf8(user_agent_bytes)?;
ret.start_height = reader.read_i32::<LittleEndian>()?;
ret.relay = reader.read_u8()? == 0x01;
Ok(ret)
}
fn write(&self, writer: &mut dyn Write) -> io::Result<()> {
writer.write_u32::<LittleEndian>(self.version)?;
writer.write_u64::<LittleEndian>(self.services)?;
writer.write_i64::<LittleEndian>(self.timestamp)?;
self.recv_addr.write(writer)?;
self.tx_addr.write(writer)?;
writer.write_u64::<LittleEndian>(self.nonce)?;
var_int::write(self.user_agent.as_bytes().len() as u64, writer)?;
writer.write(&self.user_agent.as_bytes())?;
writer.write_i32::<LittleEndian>(self.start_height)?;
writer.write_u8(if self.relay { 0x01 } else { 0x00 })?;
Ok(())
}
}
impl Payload<Version> for Version {
fn size(&self) -> usize {
33 + self.recv_addr.size()
+ self.tx_addr.size()
+ var_int::size(self.user_agent.as_bytes().len() as u64)
+ self.user_agent.as_bytes().len()
}
}
#[cfg(test)]
mod tests {
use super::*;
use hex;
use std::io::Cursor;
#[test]
fn read_bytes() {
let b = hex::decode("7f1101002500000000000000f2d2d25a00000000000000000000000000000000000000000000ffff2d32bffbdd1725000000000000000000000000000000000000000000000000008d501d3bb5369deb242f426974636f696e204142433a302e31362e30284542382e303b20626974636f7265292f6606080001".as_bytes()).unwrap();
let v = Version::read(&mut Cursor::new(&b)).unwrap();
assert!(v.version == 70015);
assert!(v.services == 37);
assert!(v.timestamp == 1523766002);
assert!(v.recv_addr.services == 0);
assert!(
v.recv_addr.ip.octets() == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 45, 50, 191, 251]
);
assert!(v.recv_addr.port == 56599);
assert!(v.tx_addr.services == 37);
assert!(v.tx_addr.ip.octets() == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert!(v.tx_addr.port == 0);
assert!(v.nonce == 16977786322265395341);
assert!(v.user_agent == "/Bitcoin ABC:0.16.0(EB8.0; bitcore)/");
assert!(v.start_height == 525926);
assert!(v.relay == true);
}
#[test]
fn write_read() {
let mut v = Vec::new();
let m = Version {
version: MIN_SUPPORTED_PROTOCOL_VERSION,
services: 77,
timestamp: 1234,
recv_addr: NodeAddr {
..Default::default()
},
tx_addr: NodeAddr {
..Default::default()
},
nonce: 99,
user_agent: "dummy".to_string(),
start_height: 22,
relay: true,
};
m.write(&mut v).unwrap();
assert!(v.len() == m.size());
assert!(Version::read(&mut Cursor::new(&v)).unwrap() == m);
}
#[test]
fn validate() {
let m = Version {
version: MIN_SUPPORTED_PROTOCOL_VERSION,
services: 77,
timestamp: secs_since(UNIX_EPOCH) as i64,
recv_addr: NodeAddr {
..Default::default()
},
tx_addr: NodeAddr {
..Default::default()
},
nonce: 99,
user_agent: "dummy".to_string(),
start_height: 22,
relay: true,
};
assert!(m.validate().is_ok());
let m2 = Version {
version: 0,
..m.clone()
};
assert!(m2.validate().is_err());
let m3 = Version {
timestamp: 0,
..m.clone()
};
assert!(m3.validate().is_err());
}
}