Skip to content

Commit

Permalink
Merge pull request #399 from elichai/2020-01-tests
Browse files Browse the repository at this point in the history
Add tests based on mutagen outputs
  • Loading branch information
stevenroose committed Feb 24, 2020
2 parents 1b946b0 + 07b30c7 commit 9cff794
Show file tree
Hide file tree
Showing 3 changed files with 235 additions and 59 deletions.
198 changes: 143 additions & 55 deletions src/consensus/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ impl From<psbt::Error> for Error {
/// Encode an object into a vector
pub fn serialize<T: Encodable + ?Sized>(data: &T) -> Vec<u8> {
let mut encoder = Cursor::new(vec![]);
data.consensus_encode(&mut encoder).unwrap();
let len = data.consensus_encode(&mut encoder).unwrap();
assert_eq!(len, encoder.get_ref().len());
encoder.into_inner()
}

Expand Down Expand Up @@ -278,7 +279,7 @@ impl<W: Write> WriteExt for W {
}
#[inline]
fn emit_bool(&mut self, v: bool) -> Result<(), Error> {
self.write_all(&[if v {1} else {0}]).map_err(Error::Io)
self.write_all(&[v as u8]).map_err(Error::Io)
}
#[inline]
fn emit_slice(&mut self, v: &[u8]) -> Result<(), Error> {
Expand Down Expand Up @@ -350,7 +351,6 @@ macro_rules! impl_int_encodable{
ReadExt::$meth_dec(&mut d).map($ty::from_le)
}
}

impl Encodable for $ty {
#[inline]
fn consensus_encode<S: WriteExt>(
Expand Down Expand Up @@ -454,15 +454,15 @@ impl Decodable for VarInt {
impl Encodable for bool {
#[inline]
fn consensus_encode<S: WriteExt>(&self, mut s: S) -> Result<usize, Error> {
s.emit_u8(if *self {1} else {0})?;
s.emit_bool(*self)?;
Ok(1)
}
}

impl Decodable for bool {
#[inline]
fn consensus_decode<D: io::Read>(mut d: D) -> Result<bool, Error> {
ReadExt::read_u8(&mut d).map(|n| n != 0)
ReadExt::read_bool(&mut d)
}
}

Expand Down Expand Up @@ -575,7 +575,6 @@ macro_rules! impl_vec {
Ok(len)
}
}

impl Decodable for Vec<$type> {
#[inline]
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
Expand Down Expand Up @@ -606,12 +605,17 @@ impl_vec!(Vec<u8>);
impl_vec!((u32, Address));
impl_vec!(u64);

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)?;
s.emit_slice(&data)?;
Ok(vi_len + data.len())
}


impl Encodable for Vec<u8> {
#[inline]
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, Error> {
let vi_len = VarInt(self.len() as u64).consensus_encode(&mut s)?;
s.emit_slice(&self)?;
Ok(vi_len + self.len())
fn consensus_encode<S: io::Write>(&self, s: S) -> Result<usize, Error> {
consensus_encode_with_size(self, s)
}
}

Expand All @@ -622,34 +626,23 @@ impl Decodable for Vec<u8> {
if len > MAX_VEC_SIZE {
return Err(self::Error::OversizedVectorAllocation { requested: len, max: MAX_VEC_SIZE })
}
let mut ret = Vec::with_capacity(len);
ret.resize(len, 0);
let mut ret = vec![0u8; len];
d.read_slice(&mut ret)?;
Ok(ret)
}
}

impl Encodable for Box<[u8]> {
#[inline]
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, Error> {
let vi_len = VarInt(self.len() as u64).consensus_encode(&mut s)?;
s.emit_slice(&self)?;
Ok(vi_len + self.len())
fn consensus_encode<S: io::Write>(&self, s: S) -> Result<usize, Error> {
consensus_encode_with_size(self, s)
}
}

impl Decodable for Box<[u8]> {
#[inline]
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
let len = VarInt::consensus_decode(&mut d)?.0;
let len = len as usize;
if len > MAX_VEC_SIZE {
return Err(self::Error::OversizedVectorAllocation { requested: len, max: MAX_VEC_SIZE })
}
let mut ret = Vec::with_capacity(len);
ret.resize(len, 0);
d.read_slice(&mut ret)?;
Ok(ret.into_boxed_slice())
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
<Vec<u8>>::consensus_decode(d).map(From::from)
}
}

Expand Down Expand Up @@ -682,8 +675,7 @@ impl Decodable for CheckedData {
});
}
let checksum = <[u8; 4]>::consensus_decode(&mut d)?;
let mut ret = Vec::with_capacity(len as usize);
ret.resize(len as usize, 0);
let mut ret = vec![0u8; len as usize];
d.read_slice(&mut ret)?;
let expected_checksum = sha2_checksum(&ret);
if expected_checksum != checksum {
Expand Down Expand Up @@ -744,9 +736,15 @@ impl Decodable for sha256d::Hash {
// Tests
#[cfg(test)]
mod tests {
use super::{CheckedData, VarInt};

use super::{deserialize, serialize, Error};
use std::{io, mem, fmt};
use std::mem::discriminant;
use super::{deserialize, serialize, Error, CheckedData, VarInt};
use super::{Transaction, BlockHash, FilterHash, TxMerkleNode, TxOut, TxIn};
use consensus::{Encodable, deserialize_partial, Decodable};
use util::endian::{u64_to_array_le, u32_to_array_le, u16_to_array_le};
use secp256k1::rand::{thread_rng, Rng};
use network::message_blockdata::Inventory;
use network::Address;

#[test]
fn serialize_int_test() {
Expand Down Expand Up @@ -805,34 +803,59 @@ mod tests {
assert_eq!(serialize(&VarInt(0xFFF)), vec![0xFDu8, 0xFF, 0xF]);
assert_eq!(serialize(&VarInt(0xF0F0F0F)), vec![0xFEu8, 0xF, 0xF, 0xF, 0xF]);
assert_eq!(serialize(&VarInt(0xF0F0F0F0F0E0)), vec![0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]);
assert_eq!(test_varint_encode(0xFF, &u64_to_array_le(0x100000000)).unwrap(), VarInt(0x100000000));
assert_eq!(test_varint_encode(0xFE, &u64_to_array_le(0x10000)).unwrap(), VarInt(0x10000));
assert_eq!(test_varint_encode(0xFD, &u64_to_array_le(0xFD)).unwrap(), VarInt(0xFD));

// Test that length calc is working correctly
test_varint_len(VarInt(0), 1);
test_varint_len(VarInt(0xFC), 1);
test_varint_len(VarInt(0xFD), 3);
test_varint_len(VarInt(0xFFFF), 3);
test_varint_len(VarInt(0x10000), 5);
test_varint_len(VarInt(0xFFFFFFFF), 5);
test_varint_len(VarInt(0xFFFFFFFF+1), 9);
test_varint_len(VarInt(u64::max_value()), 9);
}

fn test_varint_len(varint: VarInt, expected: usize) {
let mut encoder = io::Cursor::new(vec![]);
assert_eq!(varint.consensus_encode(&mut encoder).unwrap(), expected);
assert_eq!(varint.len(), expected);
}

fn test_varint_encode(n: u8, x: &[u8]) -> Result<VarInt, Error> {
let mut input = [0u8; 9];
input[0] = n;
input[1..x.len()+1].copy_from_slice(x);
deserialize_partial::<VarInt>(&input).map(|t|t.0)
}

#[test]
fn deserialize_nonminimal_vec() {
match deserialize::<Vec<u8>>(&[0xfd, 0x00, 0x00]) {
Err(Error::NonMinimalVarInt) => {},
x => panic!(x)
}
match deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]) {
Err(Error::NonMinimalVarInt) => {},
x => panic!(x)
}
match deserialize::<Vec<u8>>(&[0xfe, 0xff, 0x00, 0x00, 0x00]) {
Err(Error::NonMinimalVarInt) => {},
x => panic!(x)
}
match deserialize::<Vec<u8>>(&[0xfe, 0xff, 0xff, 0x00, 0x00]) {
Err(Error::NonMinimalVarInt) => {},
x => panic!(x)
}
match deserialize::<Vec<u8>>(&[0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) {
Err(Error::NonMinimalVarInt) => {},
x => panic!(x)
}
match deserialize::<Vec<u8>>(&[0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00]) {
Err(Error::NonMinimalVarInt) => {},
x => panic!(x)
}
// Check the edges for variant int
assert_eq!(discriminant(&test_varint_encode(0xFF, &u64_to_array_le(0x100000000-1)).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));
assert_eq!(discriminant(&test_varint_encode(0xFE, &u32_to_array_le(0x10000-1)).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));
assert_eq!(discriminant(&test_varint_encode(0xFD, &u16_to_array_le(0xFD-1)).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));

assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0x00, 0x00]).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0x00, 0x00, 0x00]).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0xff, 0x00, 0x00]).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00]).unwrap_err()),
discriminant(&Error::NonMinimalVarInt));


let mut vec_256 = vec![0; 259];
vec_256[0] = 0xfd;
Expand Down Expand Up @@ -912,6 +935,32 @@ mod tests {
assert!((deserialize(&[4u8, 2, 3, 4, 5, 6]) as Result<Vec<u8>, _>).is_err());
// found by cargo fuzz
assert!(deserialize::<Vec<u64>>(&[0xff,0xff,0xff,0xff,0x6b,0x6b,0x6b,0x6b,0x6b,0x6b,0x6b,0x6b,0x6b,0x6b,0x6b,0x6b,0xa,0xa,0x3a]).is_err());

let rand_io_err = Error::Io(io::Error::new(io::ErrorKind::Other, ""));

// Check serialization that `if len > MAX_VEC_SIZE {return err}` isn't inclusive,
// by making sure it fails with IO Error and not an `OversizedVectorAllocation` Error.
let err = deserialize::<CheckedData>(&serialize(&(super::MAX_VEC_SIZE as u32))).unwrap_err();
assert_eq!(discriminant(&err), discriminant(&rand_io_err));

test_len_is_max_vec::<u8>();
test_len_is_max_vec::<BlockHash>();
test_len_is_max_vec::<FilterHash>();
test_len_is_max_vec::<TxMerkleNode>();
test_len_is_max_vec::<Transaction>();
test_len_is_max_vec::<TxOut>();
test_len_is_max_vec::<TxIn>();
test_len_is_max_vec::<Inventory>();
test_len_is_max_vec::<Vec<u8>>();
test_len_is_max_vec::<(u32, Address)>();
test_len_is_max_vec::<u64>();
}

fn test_len_is_max_vec<T>() where Vec<T>: Decodable, T: fmt::Debug {
let rand_io_err = Error::Io(io::Error::new(io::ErrorKind::Other, ""));
let varint = VarInt((super::MAX_VEC_SIZE / mem::size_of::<T>()) as u64);
let err = deserialize::<Vec<T>>(&serialize(&varint)).unwrap_err();
assert_eq!(discriminant(&err), discriminant(&rand_io_err));
}

#[test]
Expand All @@ -928,5 +977,44 @@ mod tests {
let cd: Result<CheckedData, _> = deserialize(&[5u8, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5]);
assert_eq!(cd.ok(), Some(CheckedData(vec![1u8, 2, 3, 4, 5])));
}

#[test]
fn serialization_round_trips() {
macro_rules! round_trip {
($($val_type:ty),*) => {
$(
let r: $val_type = thread_rng().gen();
assert_eq!(deserialize::<$val_type>(&serialize(&r)).unwrap(), r);
)*
};
}
macro_rules! round_trip_bytes {
($(($val_type:ty, $data:expr)),*) => {
$(
thread_rng().fill(&mut $data[..]);
assert_eq!(deserialize::<$val_type>(&serialize(&$data)).unwrap()[..], $data[..]);
)*
};
}

let mut data = Vec::with_capacity(256);
let mut data64 = Vec::with_capacity(256);
for _ in 0..10 {
round_trip!{bool, i8, u8, i16, u16, i32, u32, i64, u64,
(bool, i8, u16, i32), (u64, i64, u32, i32, u16, i16), (i8, u8, i16, u16, i32, u32, i64, u64),
[u8; 2], [u8; 4], [u8; 8], [u8; 12], [u8; 16], [u8; 32]};

data.clear();
data64.clear();
let len = thread_rng().gen_range(1, 256);
data.resize(len, 0u8);
data64.resize(len, 0u64);
let mut arr33 = [0u8; 33];
let mut arr16 = [0u16; 8];
round_trip_bytes!{(Vec<u8>, data), ([u8; 33], arr33), ([u16; 8], arr16), (Vec<u64>, data64)};


}
}
}

0 comments on commit 9cff794

Please sign in to comment.