commit
8255e01df0
|
@ -42,7 +42,7 @@ use util::psbt;
|
|||
|
||||
use blockdata::transaction::{TxOut, Transaction, TxIn};
|
||||
use network::message_blockdata::Inventory;
|
||||
use network::address::Address;
|
||||
use network::address::{Address, AddrV2Message};
|
||||
|
||||
/// Encoding error
|
||||
#[derive(Debug)]
|
||||
|
@ -530,6 +530,7 @@ macro_rules! impl_array {
|
|||
impl_array!(2);
|
||||
impl_array!(4);
|
||||
impl_array!(8);
|
||||
impl_array!(10);
|
||||
impl_array!(12);
|
||||
impl_array!(16);
|
||||
impl_array!(32);
|
||||
|
@ -600,6 +601,7 @@ impl_vec!(Inventory);
|
|||
impl_vec!(Vec<u8>);
|
||||
impl_vec!((u32, Address));
|
||||
impl_vec!(u64);
|
||||
impl_vec!(AddrV2Message);
|
||||
|
||||
fn consensus_encode_with_size<S: io::Write>(data: &[u8], mut s: S) -> Result<usize, Error> {
|
||||
let vi_len = VarInt(data.len() as u64).consensus_encode(&mut s)?;
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
|
||||
use std::io;
|
||||
use std::fmt;
|
||||
use std::net::{SocketAddr, Ipv6Addr, SocketAddrV4, SocketAddrV6};
|
||||
use std::net::{SocketAddr, Ipv6Addr, SocketAddrV4, SocketAddrV6, Ipv4Addr};
|
||||
|
||||
use network::constants::ServiceFlags;
|
||||
use consensus::encode::{self, Decodable, Encodable};
|
||||
use consensus::encode::{self, Decodable, Encodable, VarInt, ReadExt, WriteExt};
|
||||
|
||||
/// A message which can be sent on the Bitcoin network
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -110,12 +110,167 @@ impl fmt::Debug for Address {
|
|||
}
|
||||
}
|
||||
|
||||
/// Address received from BIP155 addrv2 message
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct AddrV2Message {
|
||||
/// Time that this node was last seen as connected to the network
|
||||
pub time: u32,
|
||||
/// Service bits
|
||||
pub services: ServiceFlags,
|
||||
/// Network ID + Network Address
|
||||
pub addr: AddrV2,
|
||||
/// Network port, 0 if not applicable
|
||||
pub port: u16
|
||||
}
|
||||
|
||||
/// Supported networks for use in BIP155 addrv2 message
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum AddrV2 {
|
||||
/// IPV4
|
||||
Ipv4(Ipv4Addr),
|
||||
/// IPV6
|
||||
Ipv6(Ipv6Addr),
|
||||
/// TORV2
|
||||
TorV2([u8; 10]),
|
||||
/// TORV3
|
||||
TorV3([u8; 32]),
|
||||
/// I2P
|
||||
I2p([u8; 32]),
|
||||
/// CJDNS
|
||||
Cjdns(Ipv6Addr),
|
||||
/// Unknown
|
||||
Unknown(u8, Vec<u8>),
|
||||
}
|
||||
|
||||
impl Encodable for AddrV2 {
|
||||
fn consensus_encode<W: io::Write>(&self, e: W) -> Result<usize, encode::Error> {
|
||||
fn encode_addr<W: io::Write>(mut e: W, network: u8, bytes: &[u8]) -> Result<usize, encode::Error> {
|
||||
let len =
|
||||
network.consensus_encode(&mut e)? +
|
||||
VarInt(bytes.len() as u64).consensus_encode(&mut e)? +
|
||||
bytes.len();
|
||||
e.emit_slice(bytes)?;
|
||||
Ok(len)
|
||||
}
|
||||
Ok(match *self {
|
||||
AddrV2::Ipv4(ref addr) => encode_addr(e, 1, &addr.octets())?,
|
||||
AddrV2::Ipv6(ref addr) => encode_addr(e, 2, &addr.octets())?,
|
||||
AddrV2::TorV2(ref bytes) => encode_addr(e, 3, bytes)?,
|
||||
AddrV2::TorV3(ref bytes) => encode_addr(e, 4, bytes)?,
|
||||
AddrV2::I2p(ref bytes) => encode_addr(e, 5, bytes)?,
|
||||
AddrV2::Cjdns(ref addr) => encode_addr(e, 6, &addr.octets())?,
|
||||
AddrV2::Unknown(network, ref bytes) => encode_addr(e, network, bytes)?
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Decodable for AddrV2 {
|
||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
|
||||
let network_id = u8::consensus_decode(&mut d)?;
|
||||
let len = VarInt::consensus_decode(&mut d)?.0;
|
||||
if len > 512 {
|
||||
return Err(encode::Error::ParseFailed("IP must be <= 512 bytes"));
|
||||
}
|
||||
Ok(match network_id {
|
||||
1 => {
|
||||
if len != 4 {
|
||||
return Err(encode::Error::ParseFailed("Invalid IPv4 address"));
|
||||
}
|
||||
let addr: [u8; 4] = Decodable::consensus_decode(&mut d)?;
|
||||
AddrV2::Ipv4(Ipv4Addr::new(addr[0], addr[1], addr[2], addr[3]))
|
||||
},
|
||||
2 => {
|
||||
if len != 16 {
|
||||
return Err(encode::Error::ParseFailed("Invalid IPv6 address"));
|
||||
}
|
||||
let addr: [u16; 8] = addr_to_be(Decodable::consensus_decode(&mut d)?);
|
||||
if addr[0..3] == ONION {
|
||||
return Err(encode::Error::ParseFailed("OnionCat address sent with IPv6 network id"));
|
||||
}
|
||||
if addr[0..6] == [0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0xFFFF] {
|
||||
return Err(encode::Error::ParseFailed("IPV4 wrapped address sent with IPv6 network id"));
|
||||
}
|
||||
AddrV2::Ipv6(Ipv6Addr::new(
|
||||
addr[0],addr[1],addr[2],addr[3],
|
||||
addr[4],addr[5],addr[6],addr[7]
|
||||
))
|
||||
},
|
||||
3 => {
|
||||
if len != 10 {
|
||||
return Err(encode::Error::ParseFailed("Invalid TorV2 address"));
|
||||
}
|
||||
let id = Decodable::consensus_decode(&mut d)?;
|
||||
AddrV2::TorV2(id)
|
||||
},
|
||||
4 => {
|
||||
if len != 32 {
|
||||
return Err(encode::Error::ParseFailed("Invalid TorV3 address"));
|
||||
}
|
||||
let pubkey = Decodable::consensus_decode(&mut d)?;
|
||||
AddrV2::TorV3(pubkey)
|
||||
},
|
||||
5 => {
|
||||
if len != 32 {
|
||||
return Err(encode::Error::ParseFailed("Invalid I2P address"));
|
||||
}
|
||||
let hash = Decodable::consensus_decode(&mut d)?;
|
||||
AddrV2::I2p(hash)
|
||||
},
|
||||
6 => {
|
||||
if len != 16 {
|
||||
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
|
||||
}
|
||||
let addr: [u16; 8] = Decodable::consensus_decode(&mut d)?;
|
||||
// check the first byte for the CJDNS marker
|
||||
if addr[0] as u8 != 0xFC {
|
||||
return Err(encode::Error::ParseFailed("Invalid CJDNS address"));
|
||||
}
|
||||
let addr = addr_to_be(addr);
|
||||
AddrV2::Cjdns(Ipv6Addr::new(
|
||||
addr[0],addr[1],addr[2],addr[3],
|
||||
addr[4],addr[5],addr[6],addr[7]
|
||||
))
|
||||
},
|
||||
_ => {
|
||||
// len already checked above to be <= 512
|
||||
let mut addr = vec![0u8; len as usize];
|
||||
d.read_slice(&mut addr)?;
|
||||
AddrV2::Unknown(network_id, addr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Encodable for AddrV2Message {
|
||||
fn consensus_encode<W: io::Write>(&self, mut e: W) -> Result<usize, encode::Error> {
|
||||
let mut len = 0;
|
||||
len += self.time.consensus_encode(&mut e)?;
|
||||
len += VarInt(self.services.as_u64()).consensus_encode(&mut e)?;
|
||||
len += self.addr.consensus_encode(&mut e)?;
|
||||
len += self.port.to_be().consensus_encode(e)?;
|
||||
Ok(len)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Decodable for AddrV2Message {
|
||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
|
||||
Ok(AddrV2Message{
|
||||
time: Decodable::consensus_decode(&mut d)?,
|
||||
services: ServiceFlags::from(VarInt::consensus_decode(&mut d)?.0),
|
||||
addr: Decodable::consensus_decode(&mut d)?,
|
||||
port: u16::from_be(Decodable::consensus_decode(d)?),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::str::FromStr;
|
||||
use super::Address;
|
||||
use super::{AddrV2Message, AddrV2, Address};
|
||||
use network::constants::ServiceFlags;
|
||||
use std::net::{SocketAddr, IpAddr, Ipv4Addr, Ipv6Addr};
|
||||
use hashes::hex::FromHex;
|
||||
|
||||
use consensus::encode::{deserialize, serialize};
|
||||
|
||||
|
@ -191,5 +346,116 @@ mod test {
|
|||
let addr = Address::new(&onionaddr, ServiceFlags::NONE);
|
||||
assert!(addr.socket_addr().is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_addrv2_test() {
|
||||
// Taken from https://github.com/bitcoin/bitcoin/blob/12a1c3ad1a43634d2a98717e49e3f02c4acea2fe/src/test/net_tests.cpp#L348
|
||||
|
||||
let ip = AddrV2::Ipv4(Ipv4Addr::new(1, 2, 3, 4));
|
||||
assert_eq!(serialize(&ip), Vec::from_hex("010401020304").unwrap());
|
||||
|
||||
let ip = AddrV2::Ipv6(Ipv6Addr::from_str("1a1b:2a2b:3a3b:4a4b:5a5b:6a6b:7a7b:8a8b").unwrap());
|
||||
assert_eq!(serialize(&ip), Vec::from_hex("02101a1b2a2b3a3b4a4b5a5b6a6b7a7b8a8b").unwrap());
|
||||
|
||||
let ip = AddrV2::TorV2(FromHex::from_hex("f1f2f3f4f5f6f7f8f9fa").unwrap());
|
||||
assert_eq!(serialize(&ip), Vec::from_hex("030af1f2f3f4f5f6f7f8f9fa").unwrap());
|
||||
|
||||
let ip = AddrV2::TorV3(FromHex::from_hex("53cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88").unwrap());
|
||||
assert_eq!(serialize(&ip), Vec::from_hex("042053cd5648488c4707914182655b7664034e09e66f7e8cbf1084e654eb56c5bd88").unwrap());
|
||||
|
||||
let ip = AddrV2::I2p(FromHex::from_hex("a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap());
|
||||
assert_eq!(serialize(&ip), Vec::from_hex("0520a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap());
|
||||
|
||||
let ip = AddrV2::Cjdns(Ipv6Addr::from_str("fc00:1:2:3:4:5:6:7").unwrap());
|
||||
assert_eq!(serialize(&ip), Vec::from_hex("0610fc000001000200030004000500060007").unwrap());
|
||||
|
||||
let ip = AddrV2::Unknown(170, Vec::from_hex("01020304").unwrap());
|
||||
assert_eq!(serialize(&ip), Vec::from_hex("aa0401020304").unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialize_addrv2_test() {
|
||||
// Taken from https://github.com/bitcoin/bitcoin/blob/12a1c3ad1a43634d2a98717e49e3f02c4acea2fe/src/test/net_tests.cpp#L386
|
||||
|
||||
// Valid IPv4.
|
||||
let ip: AddrV2 = deserialize(&Vec::from_hex("010401020304").unwrap()).unwrap();
|
||||
assert_eq!(ip, AddrV2::Ipv4(Ipv4Addr::new(1, 2, 3, 4)));
|
||||
|
||||
// Invalid IPv4, valid length but address itself is shorter.
|
||||
deserialize::<AddrV2>(&Vec::from_hex("01040102").unwrap()).unwrap_err();
|
||||
|
||||
// Invalid IPv4, with bogus length.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("010501020304").unwrap()).is_err());
|
||||
|
||||
// Invalid IPv4, with extreme length.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("01fd010201020304").unwrap()).is_err());
|
||||
|
||||
// Valid IPv6.
|
||||
let ip: AddrV2 = deserialize(&Vec::from_hex("02100102030405060708090a0b0c0d0e0f10").unwrap()).unwrap();
|
||||
assert_eq!(ip, AddrV2::Ipv6(Ipv6Addr::from_str("102:304:506:708:90a:b0c:d0e:f10").unwrap()));
|
||||
|
||||
// Invalid IPv6, with bogus length.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("020400").unwrap()).is_err());
|
||||
|
||||
// Invalid IPv6, contains embedded IPv4.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("021000000000000000000000ffff01020304").unwrap()).is_err());
|
||||
|
||||
// Invalid IPv6, contains embedded TORv2.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("0210fd87d87eeb430102030405060708090a").unwrap()).is_err());
|
||||
|
||||
// Valid TORv2.
|
||||
let ip: AddrV2 = deserialize(&Vec::from_hex("030af1f2f3f4f5f6f7f8f9fa").unwrap()).unwrap();
|
||||
assert_eq!(ip, AddrV2::TorV2(FromHex::from_hex("f1f2f3f4f5f6f7f8f9fa").unwrap()));
|
||||
|
||||
// Invalid TORv2, with bogus length.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("030700").unwrap()).is_err());
|
||||
|
||||
// Valid TORv3.
|
||||
let ip: AddrV2 = deserialize(&Vec::from_hex("042079bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f").unwrap()).unwrap();
|
||||
assert_eq!(ip, AddrV2::TorV3(FromHex::from_hex("79bcc625184b05194975c28b66b66b0469f7f6556fb1ac3189a79b40dda32f1f").unwrap()));
|
||||
|
||||
// Invalid TORv3, with bogus length.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("040000").unwrap()).is_err());
|
||||
|
||||
// Valid I2P.
|
||||
let ip: AddrV2 = deserialize(&Vec::from_hex("0520a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap()).unwrap();
|
||||
assert_eq!(ip, AddrV2::I2p(FromHex::from_hex("a2894dabaec08c0051a481a6dac88b64f98232ae42d4b6fd2fa81952dfe36a87").unwrap()));
|
||||
|
||||
// Invalid I2P, with bogus length.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("050300").unwrap()).is_err());
|
||||
|
||||
// Valid CJDNS.
|
||||
let ip: AddrV2 = deserialize(&Vec::from_hex("0610fc000001000200030004000500060007").unwrap()).unwrap();
|
||||
assert_eq!(ip, AddrV2::Cjdns(Ipv6Addr::from_str("fc00:1:2:3:4:5:6:7").unwrap()));
|
||||
|
||||
// Invalid CJDNS, incorrect marker
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("0610fd000001000200030004000500060007").unwrap()).is_err());
|
||||
|
||||
// Invalid CJDNS, with bogus length.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("060100").unwrap()).is_err());
|
||||
|
||||
// Unknown, with extreme length.
|
||||
assert!(deserialize::<AddrV2>(&Vec::from_hex("aafe0000000201020304050607").unwrap()).is_err());
|
||||
|
||||
// Unknown, with reasonable length.
|
||||
let ip: AddrV2 = deserialize(&Vec::from_hex("aa0401020304").unwrap()).unwrap();
|
||||
assert_eq!(ip, AddrV2::Unknown(170, Vec::from_hex("01020304").unwrap()));
|
||||
|
||||
// Unknown, with zero length.
|
||||
let ip: AddrV2 = deserialize(&Vec::from_hex("aa00").unwrap()).unwrap();
|
||||
assert_eq!(ip, AddrV2::Unknown(170, vec![]));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn addrv2message_test() {
|
||||
let raw = Vec::from_hex("0261bc6649019902abab208d79627683fd4804010409090909208d").unwrap();
|
||||
let addresses: Vec<AddrV2Message> = deserialize(&raw).unwrap();
|
||||
|
||||
assert_eq!(addresses, vec![
|
||||
AddrV2Message{services: ServiceFlags::NETWORK, time: 0x4966bc61, port: 8333, addr: AddrV2::Unknown(153, Vec::from_hex("abab").unwrap())},
|
||||
AddrV2Message{services: ServiceFlags::NETWORK_LIMITED | ServiceFlags::WITNESS | ServiceFlags::COMPACT_FILTERS, time: 0x83766279, port: 8333, addr: AddrV2::Ipv4(Ipv4Addr::new(9, 9, 9, 9))},
|
||||
]);
|
||||
|
||||
assert_eq!(serialize(&addresses), raw);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ use std::io::Cursor;
|
|||
|
||||
use blockdata::block;
|
||||
use blockdata::transaction;
|
||||
use network::address::Address;
|
||||
use network::address::{Address, AddrV2Message};
|
||||
use network::message_network;
|
||||
use network::message_blockdata;
|
||||
use network::message_filter;
|
||||
|
@ -159,6 +159,10 @@ pub enum NetworkMessage {
|
|||
FeeFilter(i64),
|
||||
/// `wtxidrelay`
|
||||
WtxidRelay,
|
||||
/// `addrv2`
|
||||
AddrV2(Vec<AddrV2Message>),
|
||||
/// `sendaddrv2`
|
||||
SendAddrV2,
|
||||
}
|
||||
|
||||
impl NetworkMessage {
|
||||
|
@ -191,6 +195,8 @@ impl NetworkMessage {
|
|||
NetworkMessage::Reject(_) => "reject",
|
||||
NetworkMessage::FeeFilter(_) => "feefilter",
|
||||
NetworkMessage::WtxidRelay => "wtxidrelay",
|
||||
NetworkMessage::AddrV2(_) => "addrv2",
|
||||
NetworkMessage::SendAddrV2 => "sendaddrv2",
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -260,11 +266,13 @@ impl Encodable for RawNetworkMessage {
|
|||
NetworkMessage::Alert(ref dat) => serialize(dat),
|
||||
NetworkMessage::Reject(ref dat) => serialize(dat),
|
||||
NetworkMessage::FeeFilter(ref data) => serialize(data),
|
||||
NetworkMessage::AddrV2(ref dat) => serialize(dat),
|
||||
NetworkMessage::Verack
|
||||
| NetworkMessage::SendHeaders
|
||||
| NetworkMessage::MemPool
|
||||
| NetworkMessage::GetAddr
|
||||
| NetworkMessage::WtxidRelay => vec![],
|
||||
| NetworkMessage::SendAddrV2 => vec![],
|
||||
}).consensus_encode(&mut s)?;
|
||||
Ok(len)
|
||||
}
|
||||
|
@ -329,6 +337,8 @@ impl Decodable for RawNetworkMessage {
|
|||
"alert" => NetworkMessage::Alert(Decodable::consensus_decode(&mut mem_d)?),
|
||||
"feefilter" => NetworkMessage::FeeFilter(Decodable::consensus_decode(&mut mem_d)?),
|
||||
"wtxidrelay" => NetworkMessage::WtxidRelay,
|
||||
"addrv2" => NetworkMessage::AddrV2(Decodable::consensus_decode(&mut mem_d)?),
|
||||
"sendaddrv2" => NetworkMessage::SendAddrV2,
|
||||
_ => return Err(encode::Error::UnrecognizedNetworkCommand(cmd.into_owned())),
|
||||
};
|
||||
Ok(RawNetworkMessage {
|
||||
|
@ -341,13 +351,14 @@ impl Decodable for RawNetworkMessage {
|
|||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::io;
|
||||
use std::net::Ipv4Addr;
|
||||
use super::{RawNetworkMessage, NetworkMessage, CommandString};
|
||||
use network::constants::ServiceFlags;
|
||||
use consensus::encode::{Encodable, deserialize, deserialize_partial, serialize};
|
||||
use hashes::hex::FromHex;
|
||||
use hashes::sha256d::Hash;
|
||||
use hashes::Hash as HashTrait;
|
||||
use network::address::Address;
|
||||
use network::address::{Address, AddrV2, AddrV2Message};
|
||||
use super::message_network::{Reject, RejectReason, VersionMessage};
|
||||
use network::message_blockdata::{Inventory, GetBlocksMessage, GetHeadersMessage};
|
||||
use blockdata::block::{Block, BlockHeader};
|
||||
|
@ -393,6 +404,8 @@ mod test {
|
|||
NetworkMessage::Reject(Reject{message: "Test reject".into(), ccode: RejectReason::Duplicate, reason: "Cause".into(), hash: hash([255u8; 32])}),
|
||||
NetworkMessage::FeeFilter(1000),
|
||||
NetworkMessage::WtxidRelay,
|
||||
NetworkMessage::AddrV2(vec![AddrV2Message{ addr: AddrV2::Ipv4(Ipv4Addr::new(127, 0, 0, 1)), port: 0, services: ServiceFlags::NONE, time: 0 }]),
|
||||
NetworkMessage::SendAddrV2,
|
||||
];
|
||||
|
||||
for msg in msgs {
|
||||
|
|
Loading…
Reference in New Issue