Move MetaAddr serialization into zebra_network::protocol::external (#3020)
Preparation for `addrv2` serialization.
This commit is contained in:
parent
b1303ab8d7
commit
88cdf0f832
|
|
@ -3,22 +3,13 @@
|
||||||
use std::{
|
use std::{
|
||||||
cmp::{Ord, Ordering},
|
cmp::{Ord, Ordering},
|
||||||
convert::TryInto,
|
convert::TryInto,
|
||||||
io::{Read, Write},
|
|
||||||
net::SocketAddr,
|
net::SocketAddr,
|
||||||
time::Instant,
|
time::Instant,
|
||||||
};
|
};
|
||||||
|
|
||||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
use zebra_chain::serialization::{canonical_socket_addr, DateTime32};
|
||||||
|
|
||||||
use zebra_chain::serialization::{
|
use crate::{constants, protocol::types::PeerServices};
|
||||||
canonical_socket_addr, DateTime32, ReadZcashExt, SerializationError, TrustedPreallocate,
|
|
||||||
WriteZcashExt, ZcashDeserialize, ZcashDeserializeInto, ZcashSerialize,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
constants,
|
|
||||||
protocol::{external::MAX_PROTOCOL_MESSAGE_LEN, types::PeerServices},
|
|
||||||
};
|
|
||||||
|
|
||||||
use MetaAddrChange::*;
|
use MetaAddrChange::*;
|
||||||
use PeerAddrState::*;
|
use PeerAddrState::*;
|
||||||
|
|
@ -910,53 +901,3 @@ impl PartialEq for MetaAddr {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for MetaAddr {}
|
impl Eq for MetaAddr {}
|
||||||
|
|
||||||
impl ZcashSerialize for MetaAddr {
|
|
||||||
fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
|
|
||||||
self.last_seen()
|
|
||||||
.expect(
|
|
||||||
"unexpected MetaAddr with missing last seen time: MetaAddrs should be sanitized \
|
|
||||||
before serialization",
|
|
||||||
)
|
|
||||||
.zcash_serialize(&mut writer)?;
|
|
||||||
|
|
||||||
writer.write_u64::<LittleEndian>(
|
|
||||||
self.services
|
|
||||||
.expect(
|
|
||||||
"unexpected MetaAddr with missing peer services: MetaAddrs should be \
|
|
||||||
sanitized before serialization",
|
|
||||||
)
|
|
||||||
.bits(),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
writer.write_socket_addr(self.addr)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ZcashDeserialize for MetaAddr {
|
|
||||||
fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
|
|
||||||
let untrusted_last_seen = (&mut reader).zcash_deserialize_into()?;
|
|
||||||
let untrusted_services =
|
|
||||||
PeerServices::from_bits_truncate(reader.read_u64::<LittleEndian>()?);
|
|
||||||
let addr = reader.read_socket_addr()?;
|
|
||||||
|
|
||||||
Ok(MetaAddr::new_gossiped_meta_addr(
|
|
||||||
addr,
|
|
||||||
untrusted_services,
|
|
||||||
untrusted_last_seen,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A serialized meta addr has a 4 byte time, 8 byte services, 16 byte IP addr, and 2 byte port
|
|
||||||
const META_ADDR_SIZE: usize = 4 + 8 + 16 + 2;
|
|
||||||
|
|
||||||
impl TrustedPreallocate for MetaAddr {
|
|
||||||
fn max_allocation() -> u64 {
|
|
||||||
// Since a maximal serialized Vec<MetAddr> uses at least three bytes for its length (2MB messages / 30B MetaAddr implies the maximal length is much greater than 253)
|
|
||||||
// the max allocation can never exceed (MAX_PROTOCOL_MESSAGE_LEN - 3) / META_ADDR_SIZE
|
|
||||||
((MAX_PROTOCOL_MESSAGE_LEN - 3) / META_ADDR_SIZE) as u64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
mod check;
|
mod check;
|
||||||
mod preallocate;
|
|
||||||
mod prop;
|
mod prop;
|
||||||
mod vectors;
|
mod vectors;
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
//! Tests for trusted preallocation during deserialization.
|
|
||||||
|
|
||||||
use super::super::{MetaAddr, META_ADDR_SIZE};
|
|
||||||
|
|
||||||
use zebra_chain::serialization::{TrustedPreallocate, ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN};
|
|
||||||
|
|
||||||
use proptest::prelude::*;
|
|
||||||
use std::convert::TryInto;
|
|
||||||
|
|
||||||
proptest! {
|
|
||||||
/// Confirm that each MetaAddr takes exactly META_ADDR_SIZE bytes when serialized.
|
|
||||||
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
|
||||||
#[test]
|
|
||||||
fn meta_addr_size_is_correct(addr in MetaAddr::arbitrary()) {
|
|
||||||
zebra_test::init();
|
|
||||||
|
|
||||||
// We require sanitization before serialization
|
|
||||||
let addr = addr.sanitize();
|
|
||||||
prop_assume!(addr.is_some());
|
|
||||||
let addr = addr.unwrap();
|
|
||||||
|
|
||||||
let serialized = addr
|
|
||||||
.zcash_serialize_to_vec()
|
|
||||||
.expect("Serialization to vec must succeed");
|
|
||||||
assert!(serialized.len() == META_ADDR_SIZE)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verifies that...
|
|
||||||
/// 1. The smallest disallowed vector of `MetaAddrs`s is too large to fit in a legal Zcash message
|
|
||||||
/// 2. The largest allowed vector is small enough to fit in a legal Zcash message
|
|
||||||
#[test]
|
|
||||||
fn meta_addr_max_allocation_is_correct(addr in MetaAddr::arbitrary()) {
|
|
||||||
zebra_test::init();
|
|
||||||
|
|
||||||
// We require sanitization before serialization
|
|
||||||
let addr = addr.sanitize();
|
|
||||||
prop_assume!(addr.is_some());
|
|
||||||
let addr = addr.unwrap();
|
|
||||||
|
|
||||||
let max_allocation: usize = MetaAddr::max_allocation().try_into().unwrap();
|
|
||||||
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
|
|
||||||
for _ in 0..(MetaAddr::max_allocation() + 1) {
|
|
||||||
smallest_disallowed_vec.push(addr);
|
|
||||||
}
|
|
||||||
let smallest_disallowed_serialized = smallest_disallowed_vec
|
|
||||||
.zcash_serialize_to_vec()
|
|
||||||
.expect("Serialization to vec must succeed");
|
|
||||||
// Check that our smallest_disallowed_vec is only one item larger than the limit
|
|
||||||
assert!(((smallest_disallowed_vec.len() - 1) as u64) == MetaAddr::max_allocation());
|
|
||||||
// Check that our smallest_disallowed_vec is too big to send in a valid Zcash message
|
|
||||||
assert!(smallest_disallowed_serialized.len() > MAX_PROTOCOL_MESSAGE_LEN);
|
|
||||||
|
|
||||||
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
|
|
||||||
smallest_disallowed_vec.pop();
|
|
||||||
let largest_allowed_vec = smallest_disallowed_vec;
|
|
||||||
let largest_allowed_serialized = largest_allowed_vec
|
|
||||||
.zcash_serialize_to_vec()
|
|
||||||
.expect("Serialization to vec must succeed");
|
|
||||||
|
|
||||||
// Check that our largest_allowed_vec contains the maximum number of MetaAddrs
|
|
||||||
assert!((largest_allowed_vec.len() as u64) == MetaAddr::max_allocation());
|
|
||||||
// Check that our largest_allowed_vec is small enough to fit in a Zcash message.
|
|
||||||
assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,3 +1,7 @@
|
||||||
|
//! Network protocol types and serialization for the Zcash wire format.
|
||||||
|
|
||||||
|
/// Node address wire formats.
|
||||||
|
mod addr;
|
||||||
/// A Tokio codec that transforms an `AsyncRead` into a `Stream` of `Message`s.
|
/// A Tokio codec that transforms an `AsyncRead` into a `Stream` of `Message`s.
|
||||||
pub mod codec;
|
pub mod codec;
|
||||||
/// Inventory items.
|
/// Inventory items.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
//! Node address types and serialization for the Zcash wire format.
|
||||||
|
|
||||||
|
use std::io::{Read, Write};
|
||||||
|
|
||||||
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
|
|
||||||
|
use zebra_chain::serialization::{
|
||||||
|
ReadZcashExt, SerializationError, TrustedPreallocate, WriteZcashExt, ZcashDeserialize,
|
||||||
|
ZcashDeserializeInto, ZcashSerialize,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
meta_addr::MetaAddr,
|
||||||
|
protocol::external::{types::PeerServices, MAX_PROTOCOL_MESSAGE_LEN},
|
||||||
|
};
|
||||||
|
|
||||||
|
impl ZcashSerialize for MetaAddr {
|
||||||
|
fn zcash_serialize<W: Write>(&self, mut writer: W) -> Result<(), std::io::Error> {
|
||||||
|
self.last_seen()
|
||||||
|
.expect(
|
||||||
|
"unexpected MetaAddr with missing last seen time: MetaAddrs should be sanitized \
|
||||||
|
before serialization",
|
||||||
|
)
|
||||||
|
.zcash_serialize(&mut writer)?;
|
||||||
|
|
||||||
|
writer.write_u64::<LittleEndian>(
|
||||||
|
self.services
|
||||||
|
.expect(
|
||||||
|
"unexpected MetaAddr with missing peer services: MetaAddrs should be \
|
||||||
|
sanitized before serialization",
|
||||||
|
)
|
||||||
|
.bits(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
writer.write_socket_addr(self.addr)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ZcashDeserialize for MetaAddr {
|
||||||
|
fn zcash_deserialize<R: Read>(mut reader: R) -> Result<Self, SerializationError> {
|
||||||
|
let untrusted_last_seen = (&mut reader).zcash_deserialize_into()?;
|
||||||
|
let untrusted_services =
|
||||||
|
PeerServices::from_bits_truncate(reader.read_u64::<LittleEndian>()?);
|
||||||
|
let addr = reader.read_socket_addr()?;
|
||||||
|
|
||||||
|
Ok(MetaAddr::new_gossiped_meta_addr(
|
||||||
|
addr,
|
||||||
|
untrusted_services,
|
||||||
|
untrusted_last_seen,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A serialized meta addr has a 4 byte time, 8 byte services, 16 byte IP addr, and 2 byte port
|
||||||
|
pub(super) const META_ADDR_SIZE: usize = 4 + 8 + 16 + 2;
|
||||||
|
|
||||||
|
impl TrustedPreallocate for MetaAddr {
|
||||||
|
fn max_allocation() -> u64 {
|
||||||
|
// Since a maximal serialized Vec<MetAddr> uses at least three bytes for its length (2MB messages / 30B MetaAddr implies the maximal length is much greater than 253)
|
||||||
|
// the max allocation can never exceed (MAX_PROTOCOL_MESSAGE_LEN - 3) / META_ADDR_SIZE
|
||||||
|
((MAX_PROTOCOL_MESSAGE_LEN - 3) / META_ADDR_SIZE) as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
//! A Tokio codec mapping byte streams to Bitcoin message streams.
|
//! A Tokio codec mapping byte streams to Bitcoin message streams.
|
||||||
|
|
||||||
use std::fmt;
|
|
||||||
use std::{
|
use std::{
|
||||||
cmp::min,
|
cmp::min,
|
||||||
|
fmt,
|
||||||
io::{Cursor, Read, Write},
|
io::{Cursor, Read, Write},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
//! Tests for trusted preallocation during deserialization.
|
//! Tests for trusted preallocation during deserialization.
|
||||||
|
|
||||||
use super::super::inv::InventoryHash;
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use proptest::prelude::*;
|
||||||
|
|
||||||
use zebra_chain::serialization::{TrustedPreallocate, ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN};
|
use zebra_chain::serialization::{TrustedPreallocate, ZcashSerialize, MAX_PROTOCOL_MESSAGE_LEN};
|
||||||
|
|
||||||
use proptest::prelude::*;
|
use crate::meta_addr::MetaAddr;
|
||||||
use std::convert::TryInto;
|
|
||||||
|
use super::super::{addr::META_ADDR_SIZE, inv::InventoryHash};
|
||||||
|
|
||||||
proptest! {
|
proptest! {
|
||||||
/// Confirm that each InventoryHash takes the expected size in bytes when serialized.
|
/// Confirm that each InventoryHash takes the expected size in bytes when serialized.
|
||||||
|
|
@ -58,3 +61,60 @@ proptest! {
|
||||||
assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
|
assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
proptest! {
|
||||||
|
/// Confirm that each MetaAddr takes exactly META_ADDR_SIZE bytes when serialized.
|
||||||
|
/// This verifies that our calculated `TrustedPreallocate::max_allocation()` is indeed an upper bound.
|
||||||
|
#[test]
|
||||||
|
fn meta_addr_size_is_correct(addr in MetaAddr::arbitrary()) {
|
||||||
|
zebra_test::init();
|
||||||
|
|
||||||
|
// We require sanitization before serialization
|
||||||
|
let addr = addr.sanitize();
|
||||||
|
prop_assume!(addr.is_some());
|
||||||
|
let addr = addr.unwrap();
|
||||||
|
|
||||||
|
let serialized = addr
|
||||||
|
.zcash_serialize_to_vec()
|
||||||
|
.expect("Serialization to vec must succeed");
|
||||||
|
assert!(serialized.len() == META_ADDR_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies that...
|
||||||
|
/// 1. The smallest disallowed vector of `MetaAddrs`s is too large to fit in a legal Zcash message
|
||||||
|
/// 2. The largest allowed vector is small enough to fit in a legal Zcash message
|
||||||
|
#[test]
|
||||||
|
fn meta_addr_max_allocation_is_correct(addr in MetaAddr::arbitrary()) {
|
||||||
|
zebra_test::init();
|
||||||
|
|
||||||
|
// We require sanitization before serialization
|
||||||
|
let addr = addr.sanitize();
|
||||||
|
prop_assume!(addr.is_some());
|
||||||
|
let addr = addr.unwrap();
|
||||||
|
|
||||||
|
let max_allocation: usize = MetaAddr::max_allocation().try_into().unwrap();
|
||||||
|
let mut smallest_disallowed_vec = Vec::with_capacity(max_allocation + 1);
|
||||||
|
for _ in 0..(MetaAddr::max_allocation() + 1) {
|
||||||
|
smallest_disallowed_vec.push(addr);
|
||||||
|
}
|
||||||
|
let smallest_disallowed_serialized = smallest_disallowed_vec
|
||||||
|
.zcash_serialize_to_vec()
|
||||||
|
.expect("Serialization to vec must succeed");
|
||||||
|
// Check that our smallest_disallowed_vec is only one item larger than the limit
|
||||||
|
assert!(((smallest_disallowed_vec.len() - 1) as u64) == MetaAddr::max_allocation());
|
||||||
|
// Check that our smallest_disallowed_vec is too big to send in a valid Zcash message
|
||||||
|
assert!(smallest_disallowed_serialized.len() > MAX_PROTOCOL_MESSAGE_LEN);
|
||||||
|
|
||||||
|
// Create largest_allowed_vec by removing one element from smallest_disallowed_vec without copying (for efficiency)
|
||||||
|
smallest_disallowed_vec.pop();
|
||||||
|
let largest_allowed_vec = smallest_disallowed_vec;
|
||||||
|
let largest_allowed_serialized = largest_allowed_vec
|
||||||
|
.zcash_serialize_to_vec()
|
||||||
|
.expect("Serialization to vec must succeed");
|
||||||
|
|
||||||
|
// Check that our largest_allowed_vec contains the maximum number of MetaAddrs
|
||||||
|
assert!((largest_allowed_vec.len() as u64) == MetaAddr::max_allocation());
|
||||||
|
// Check that our largest_allowed_vec is small enough to fit in a Zcash message.
|
||||||
|
assert!(largest_allowed_serialized.len() <= MAX_PROTOCOL_MESSAGE_LEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue