Merge pull request #399 from elichai/2020-01-tests
Add tests based on mutagen outputs
This commit is contained in:
commit
9cff794a09
|
@ -150,7 +150,8 @@ impl From<psbt::Error> for Error {
|
||||||
/// Encode an object into a vector
|
/// Encode an object into a vector
|
||||||
pub fn serialize<T: Encodable + ?Sized>(data: &T) -> Vec<u8> {
|
pub fn serialize<T: Encodable + ?Sized>(data: &T) -> Vec<u8> {
|
||||||
let mut encoder = Cursor::new(vec![]);
|
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()
|
encoder.into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,7 +279,7 @@ impl<W: Write> WriteExt for W {
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn emit_bool(&mut self, v: bool) -> Result<(), Error> {
|
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]
|
#[inline]
|
||||||
fn emit_slice(&mut self, v: &[u8]) -> Result<(), Error> {
|
fn emit_slice(&mut self, v: &[u8]) -> Result<(), Error> {
|
||||||
|
@ -350,7 +351,6 @@ macro_rules! impl_int_encodable{
|
||||||
ReadExt::$meth_dec(&mut d).map($ty::from_le)
|
ReadExt::$meth_dec(&mut d).map($ty::from_le)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Encodable for $ty {
|
impl Encodable for $ty {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode<S: WriteExt>(
|
fn consensus_encode<S: WriteExt>(
|
||||||
|
@ -454,7 +454,7 @@ impl Decodable for VarInt {
|
||||||
impl Encodable for bool {
|
impl Encodable for bool {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode<S: WriteExt>(&self, mut s: S) -> Result<usize, Error> {
|
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)
|
Ok(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -462,7 +462,7 @@ impl Encodable for bool {
|
||||||
impl Decodable for bool {
|
impl Decodable for bool {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<bool, Error> {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,7 +575,6 @@ macro_rules! impl_vec {
|
||||||
Ok(len)
|
Ok(len)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decodable for Vec<$type> {
|
impl Decodable for Vec<$type> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
|
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
|
||||||
|
@ -606,12 +605,17 @@ impl_vec!(Vec<u8>);
|
||||||
impl_vec!((u32, Address));
|
impl_vec!((u32, Address));
|
||||||
impl_vec!(u64);
|
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> {
|
impl Encodable for Vec<u8> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, Error> {
|
fn consensus_encode<S: io::Write>(&self, s: S) -> Result<usize, Error> {
|
||||||
let vi_len = VarInt(self.len() as u64).consensus_encode(&mut s)?;
|
consensus_encode_with_size(self, s)
|
||||||
s.emit_slice(&self)?;
|
|
||||||
Ok(vi_len + self.len())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -622,8 +626,7 @@ impl Decodable for Vec<u8> {
|
||||||
if len > MAX_VEC_SIZE {
|
if len > MAX_VEC_SIZE {
|
||||||
return Err(self::Error::OversizedVectorAllocation { requested: len, max: MAX_VEC_SIZE })
|
return Err(self::Error::OversizedVectorAllocation { requested: len, max: MAX_VEC_SIZE })
|
||||||
}
|
}
|
||||||
let mut ret = Vec::with_capacity(len);
|
let mut ret = vec![0u8; len];
|
||||||
ret.resize(len, 0);
|
|
||||||
d.read_slice(&mut ret)?;
|
d.read_slice(&mut ret)?;
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
@ -631,25 +634,15 @@ impl Decodable for Vec<u8> {
|
||||||
|
|
||||||
impl Encodable for Box<[u8]> {
|
impl Encodable for Box<[u8]> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, Error> {
|
fn consensus_encode<S: io::Write>(&self, s: S) -> Result<usize, Error> {
|
||||||
let vi_len = VarInt(self.len() as u64).consensus_encode(&mut s)?;
|
consensus_encode_with_size(self, s)
|
||||||
s.emit_slice(&self)?;
|
|
||||||
Ok(vi_len + self.len())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Decodable for Box<[u8]> {
|
impl Decodable for Box<[u8]> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, Error> {
|
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
|
||||||
let len = VarInt::consensus_decode(&mut d)?.0;
|
<Vec<u8>>::consensus_decode(d).map(From::from)
|
||||||
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())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,8 +675,7 @@ impl Decodable for CheckedData {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let checksum = <[u8; 4]>::consensus_decode(&mut d)?;
|
let checksum = <[u8; 4]>::consensus_decode(&mut d)?;
|
||||||
let mut ret = Vec::with_capacity(len as usize);
|
let mut ret = vec![0u8; len as usize];
|
||||||
ret.resize(len as usize, 0);
|
|
||||||
d.read_slice(&mut ret)?;
|
d.read_slice(&mut ret)?;
|
||||||
let expected_checksum = sha2_checksum(&ret);
|
let expected_checksum = sha2_checksum(&ret);
|
||||||
if expected_checksum != checksum {
|
if expected_checksum != checksum {
|
||||||
|
@ -744,9 +736,15 @@ impl Decodable for sha256d::Hash {
|
||||||
// Tests
|
// Tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{CheckedData, VarInt};
|
use std::{io, mem, fmt};
|
||||||
|
use std::mem::discriminant;
|
||||||
use super::{deserialize, serialize, Error};
|
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]
|
#[test]
|
||||||
fn serialize_int_test() {
|
fn serialize_int_test() {
|
||||||
|
@ -805,34 +803,59 @@ mod tests {
|
||||||
assert_eq!(serialize(&VarInt(0xFFF)), vec![0xFDu8, 0xFF, 0xF]);
|
assert_eq!(serialize(&VarInt(0xFFF)), vec![0xFDu8, 0xFF, 0xF]);
|
||||||
assert_eq!(serialize(&VarInt(0xF0F0F0F)), vec![0xFEu8, 0xF, 0xF, 0xF, 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!(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]
|
#[test]
|
||||||
fn deserialize_nonminimal_vec() {
|
fn deserialize_nonminimal_vec() {
|
||||||
match deserialize::<Vec<u8>>(&[0xfd, 0x00, 0x00]) {
|
// Check the edges for variant int
|
||||||
Err(Error::NonMinimalVarInt) => {},
|
assert_eq!(discriminant(&test_varint_encode(0xFF, &u64_to_array_le(0x100000000-1)).unwrap_err()),
|
||||||
x => panic!(x)
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
}
|
assert_eq!(discriminant(&test_varint_encode(0xFE, &u32_to_array_le(0x10000-1)).unwrap_err()),
|
||||||
match deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]) {
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
Err(Error::NonMinimalVarInt) => {},
|
assert_eq!(discriminant(&test_varint_encode(0xFD, &u16_to_array_le(0xFD-1)).unwrap_err()),
|
||||||
x => panic!(x)
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
}
|
|
||||||
match deserialize::<Vec<u8>>(&[0xfe, 0xff, 0x00, 0x00, 0x00]) {
|
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0x00, 0x00]).unwrap_err()),
|
||||||
Err(Error::NonMinimalVarInt) => {},
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
x => panic!(x)
|
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
|
||||||
}
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
match deserialize::<Vec<u8>>(&[0xfe, 0xff, 0xff, 0x00, 0x00]) {
|
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfd, 0xfc, 0x00]).unwrap_err()),
|
||||||
Err(Error::NonMinimalVarInt) => {},
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
x => panic!(x)
|
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0x00, 0x00, 0x00]).unwrap_err()),
|
||||||
}
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
match deserialize::<Vec<u8>>(&[0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) {
|
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xfe, 0xff, 0xff, 0x00, 0x00]).unwrap_err()),
|
||||||
Err(Error::NonMinimalVarInt) => {},
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
x => panic!(x)
|
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]).unwrap_err()),
|
||||||
}
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
match deserialize::<Vec<u8>>(&[0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00]) {
|
assert_eq!(discriminant(&deserialize::<Vec<u8>>(&[0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00]).unwrap_err()),
|
||||||
Err(Error::NonMinimalVarInt) => {},
|
discriminant(&Error::NonMinimalVarInt));
|
||||||
x => panic!(x)
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut vec_256 = vec![0; 259];
|
let mut vec_256 = vec![0; 259];
|
||||||
vec_256[0] = 0xfd;
|
vec_256[0] = 0xfd;
|
||||||
|
@ -912,6 +935,32 @@ mod tests {
|
||||||
assert!((deserialize(&[4u8, 2, 3, 4, 5, 6]) as Result<Vec<u8>, _>).is_err());
|
assert!((deserialize(&[4u8, 2, 3, 4, 5, 6]) as Result<Vec<u8>, _>).is_err());
|
||||||
// found by cargo fuzz
|
// 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());
|
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]
|
#[test]
|
||||||
|
@ -928,5 +977,44 @@ mod tests {
|
||||||
let cd: Result<CheckedData, _> = deserialize(&[5u8, 0, 0, 0, 162, 107, 175, 90, 1, 2, 3, 4, 5]);
|
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])));
|
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)};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -668,7 +668,11 @@ impl SignedAmount {
|
||||||
///
|
///
|
||||||
/// Does not include the denomination.
|
/// Does not include the denomination.
|
||||||
pub fn fmt_value_in(&self, f: &mut fmt::Write, denom: Denomination) -> fmt::Result {
|
pub fn fmt_value_in(&self, f: &mut fmt::Write, denom: Denomination) -> fmt::Result {
|
||||||
fmt_satoshi_in(self.as_sat().abs() as u64, self.is_negative(), f, denom)
|
let sats = self.as_sat().checked_abs().map(|a: i64| a as u64).unwrap_or_else(|| {
|
||||||
|
// We could also hard code this into `9223372036854775808`
|
||||||
|
u64::max_value() - self.as_sat() as u64 +1
|
||||||
|
});
|
||||||
|
fmt_satoshi_in(sats, self.is_negative(), f, denom)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a string number of this [SignedAmount] in the given denomination.
|
/// Get a string number of this [SignedAmount] in the given denomination.
|
||||||
|
@ -717,6 +721,13 @@ impl SignedAmount {
|
||||||
self.0.is_negative()
|
self.0.is_negative()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get the absolute value of this [SignedAmount].
|
||||||
|
/// Returns [None] if overflow occurred. (`self == min_value()`)
|
||||||
|
pub fn checked_abs(self) -> Option<SignedAmount> {
|
||||||
|
self.0.checked_abs().map(SignedAmount)
|
||||||
|
}
|
||||||
|
|
||||||
/// Checked addition.
|
/// Checked addition.
|
||||||
/// Returns [None] if overflow occurred.
|
/// Returns [None] if overflow occurred.
|
||||||
pub fn checked_add(self, rhs: SignedAmount) -> Option<SignedAmount> {
|
pub fn checked_add(self, rhs: SignedAmount) -> Option<SignedAmount> {
|
||||||
|
@ -1147,6 +1158,7 @@ mod tests {
|
||||||
fn parsing() {
|
fn parsing() {
|
||||||
use super::ParseAmountError as E;
|
use super::ParseAmountError as E;
|
||||||
let btc = Denomination::Bitcoin;
|
let btc = Denomination::Bitcoin;
|
||||||
|
let sat = Denomination::Satoshi;
|
||||||
let p = Amount::from_str_in;
|
let p = Amount::from_str_in;
|
||||||
let sp = SignedAmount::from_str_in;
|
let sp = SignedAmount::from_str_in;
|
||||||
|
|
||||||
|
@ -1163,11 +1175,26 @@ mod tests {
|
||||||
assert_eq!(p("1", btc), Ok(Amount::from_sat(1_000_000_00)));
|
assert_eq!(p("1", btc), Ok(Amount::from_sat(1_000_000_00)));
|
||||||
assert_eq!(sp("-.5", btc), Ok(SignedAmount::from_sat(-500_000_00)));
|
assert_eq!(sp("-.5", btc), Ok(SignedAmount::from_sat(-500_000_00)));
|
||||||
assert_eq!(p("1.1", btc), Ok(Amount::from_sat(1_100_000_00)));
|
assert_eq!(p("1.1", btc), Ok(Amount::from_sat(1_100_000_00)));
|
||||||
|
assert_eq!(p("100", sat), Ok(Amount::from_sat(100)));
|
||||||
|
assert_eq!(p("55", sat), Ok(Amount::from_sat(55)));
|
||||||
|
assert_eq!(p("5500000000000000000", sat), Ok(Amount::from_sat(5_500_000_000_000_000_000)));
|
||||||
|
// Should this even pass?
|
||||||
|
assert_eq!(p("5500000000000000000.", sat), Ok(Amount::from_sat(5_500_000_000_000_000_000)));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
p("12345678901.12345678", btc),
|
p("12345678901.12345678", btc),
|
||||||
Ok(Amount::from_sat(12_345_678_901__123_456_78))
|
Ok(Amount::from_sat(12_345_678_901__123_456_78))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// make sure satoshi > i64::max_value() is checked.
|
||||||
|
let amount = Amount::from_sat(i64::max_value() as u64);
|
||||||
|
assert_eq!(Amount::from_str_in(&amount.to_string_in(sat), sat), Ok(amount));
|
||||||
|
assert_eq!(Amount::from_str_in(&(amount+Amount(1)).to_string_in(sat), sat), Err(E::TooBig));
|
||||||
|
|
||||||
assert_eq!(p("12.000", Denomination::MilliSatoshi), Err(E::TooPrecise));
|
assert_eq!(p("12.000", Denomination::MilliSatoshi), Err(E::TooPrecise));
|
||||||
|
// exactly 50 chars.
|
||||||
|
assert_eq!(p("100000000000000.0000000000000000000000000000000000", Denomination::Bitcoin), Err(E::TooBig));
|
||||||
|
// more than 50 chars.
|
||||||
|
assert_eq!(p("100000000000000.00000000000000000000000000000000000", Denomination::Bitcoin), Err(E::InputTooLarge));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -1192,6 +1219,27 @@ mod tests {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_unsigned_signed_conversion() {
|
||||||
|
use super::ParseAmountError as E;
|
||||||
|
let sa = SignedAmount::from_sat;
|
||||||
|
let ua = Amount::from_sat;
|
||||||
|
|
||||||
|
assert_eq!(Amount::max_value().to_signed(), Err(E::TooBig));
|
||||||
|
assert_eq!(ua(i64::max_value() as u64).to_signed(), Ok(sa(i64::max_value())));
|
||||||
|
assert_eq!(ua(0).to_signed(), Ok(sa(0)));
|
||||||
|
assert_eq!(ua(1).to_signed(), Ok( sa(1)));
|
||||||
|
assert_eq!(ua(1).to_signed(), Ok(sa(1)));
|
||||||
|
assert_eq!(ua(i64::max_value() as u64 + 1).to_signed(), Err(E::TooBig));
|
||||||
|
|
||||||
|
assert_eq!(sa(-1).to_unsigned(), Err(E::Negative));
|
||||||
|
assert_eq!(sa(i64::max_value()).to_unsigned(), Ok(ua(i64::max_value() as u64)));
|
||||||
|
|
||||||
|
assert_eq!(sa(0).to_unsigned().unwrap().to_signed(), Ok(sa(0)));
|
||||||
|
assert_eq!(sa(1).to_unsigned().unwrap().to_signed(), Ok(sa(1)));
|
||||||
|
assert_eq!(sa(i64::max_value()).to_unsigned().unwrap().to_signed(), Ok(sa(i64::max_value())));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_str() {
|
fn from_str() {
|
||||||
use super::ParseAmountError as E;
|
use super::ParseAmountError as E;
|
||||||
|
@ -1230,10 +1278,39 @@ mod tests {
|
||||||
assert_eq!(sp("-100 bits"), Ok(SignedAmount::from_sat(-10_000)));
|
assert_eq!(sp("-100 bits"), Ok(SignedAmount::from_sat(-10_000)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_from_string_in() {
|
||||||
|
use super::Denomination as D;
|
||||||
|
let ua_str = Amount::from_str_in;
|
||||||
|
let ua_sat = Amount::from_sat;
|
||||||
|
let sa_str = SignedAmount::from_str_in;
|
||||||
|
let sa_sat = SignedAmount::from_sat;
|
||||||
|
|
||||||
|
assert_eq!("0.50", Amount::from_sat(50).to_string_in(D::Bit));
|
||||||
|
assert_eq!("-0.50", SignedAmount::from_sat(-50).to_string_in(D::Bit));
|
||||||
|
assert_eq!("0.00253583", Amount::from_sat(253583).to_string_in(D::Bitcoin));
|
||||||
|
assert_eq!("-5", SignedAmount::from_sat(-5).to_string_in(D::Satoshi));
|
||||||
|
assert_eq!("0.10000000", Amount::from_sat(100_000_00).to_string_in(D::Bitcoin));
|
||||||
|
assert_eq!("-100.00", SignedAmount::from_sat(-10_000).to_string_in(D::Bit));
|
||||||
|
|
||||||
|
assert_eq!(ua_str(&ua_sat(0).to_string_in(D::Satoshi), D::Satoshi), Ok(ua_sat(0)));
|
||||||
|
assert_eq!(ua_str(&ua_sat(500).to_string_in(D::Bitcoin), D::Bitcoin), Ok(ua_sat(500)));
|
||||||
|
assert_eq!(ua_str(&ua_sat(21_000_000).to_string_in(D::Bit), D::Bit), Ok(ua_sat(21_000_000)));
|
||||||
|
assert_eq!(ua_str(&ua_sat(1).to_string_in(D::MicroBitcoin), D::MicroBitcoin), Ok(ua_sat(1)));
|
||||||
|
assert_eq!(ua_str(&ua_sat(1_000_000_000_000).to_string_in(D::MilliBitcoin), D::MilliBitcoin), Ok(ua_sat(1_000_000_000_000)));
|
||||||
|
assert_eq!(ua_str(&ua_sat(u64::max_value()).to_string_in(D::MilliBitcoin), D::MilliBitcoin), Err(ParseAmountError::TooBig));
|
||||||
|
|
||||||
|
assert_eq!(sa_str(&sa_sat(-1).to_string_in(D::MicroBitcoin), D::MicroBitcoin), Ok(sa_sat(-1)));
|
||||||
|
|
||||||
|
assert_eq!(sa_str(&sa_sat(i64::max_value()).to_string_in(D::Satoshi), D::MicroBitcoin), Err(ParseAmountError::TooBig));
|
||||||
|
// Test an overflow bug in `abs()`
|
||||||
|
assert_eq!(sa_str(&sa_sat(i64::min_value()).to_string_in(D::Satoshi), D::MicroBitcoin), Err(ParseAmountError::TooBig));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn to_string_with_denomination_from_str_roundtrip() {
|
fn to_string_with_denomination_from_str_roundtrip() {
|
||||||
use super::Denomination as D;
|
use super::Denomination as D;
|
||||||
|
|
||||||
let amt = Amount::from_sat(42);
|
let amt = Amount::from_sat(42);
|
||||||
let denom = Amount::to_string_with_denomination;
|
let denom = Amount::to_string_with_denomination;
|
||||||
assert_eq!(Amount::from_str(&denom(&amt, D::Bitcoin)), Ok(amt));
|
assert_eq!(Amount::from_str(&denom(&amt, D::Bitcoin)), Ok(amt));
|
||||||
|
@ -1242,6 +1319,9 @@ mod tests {
|
||||||
assert_eq!(Amount::from_str(&denom(&amt, D::Bit)), Ok(amt));
|
assert_eq!(Amount::from_str(&denom(&amt, D::Bit)), Ok(amt));
|
||||||
assert_eq!(Amount::from_str(&denom(&amt, D::Satoshi)), Ok(amt));
|
assert_eq!(Amount::from_str(&denom(&amt, D::Satoshi)), Ok(amt));
|
||||||
assert_eq!(Amount::from_str(&denom(&amt, D::MilliSatoshi)), Ok(amt));
|
assert_eq!(Amount::from_str(&denom(&amt, D::MilliSatoshi)), Ok(amt));
|
||||||
|
|
||||||
|
assert_eq!(Amount::from_str("42 satoshi BTC"), Err(ParseAmountError::InvalidFormat));
|
||||||
|
assert_eq!(SignedAmount::from_str("-42 satoshi BTC"), Err(ParseAmountError::InvalidFormat));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
|
|
|
@ -131,7 +131,7 @@ pub fn from(data: &str) -> Result<Vec<u8>, Error> {
|
||||||
// Build in base 256
|
// Build in base 256
|
||||||
for d58 in data.bytes() {
|
for d58 in data.bytes() {
|
||||||
// Compute "X = X * 58 + next_digit" in base 256
|
// Compute "X = X * 58 + next_digit" in base 256
|
||||||
if d58 as usize > BASE58_DIGITS.len() {
|
if d58 as usize >= BASE58_DIGITS.len() {
|
||||||
return Err(Error::BadByte(d58));
|
return Err(Error::BadByte(d58));
|
||||||
}
|
}
|
||||||
let mut carry = match BASE58_DIGITS[d58 as usize] {
|
let mut carry = match BASE58_DIGITS[d58 as usize] {
|
||||||
|
@ -294,7 +294,9 @@ mod tests {
|
||||||
|
|
||||||
// Addresses
|
// Addresses
|
||||||
assert_eq!(from_check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH").ok(),
|
assert_eq!(from_check("1PfJpZsjreyVrqeoAfabrRwwjQyoSQMmHH").ok(),
|
||||||
Some(Vec::from_hex("00f8917303bfa8ef24f292e8fa1419b20460ba064d").unwrap()))
|
Some(Vec::from_hex("00f8917303bfa8ef24f292e8fa1419b20460ba064d").unwrap()));
|
||||||
|
// Non Base58 char.
|
||||||
|
assert_eq!(from("¢").unwrap_err(), Error::BadByte(194));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -303,6 +305,12 @@ mod tests {
|
||||||
let v: Vec<u8> = from_check(s).unwrap();
|
let v: Vec<u8> = from_check(s).unwrap();
|
||||||
assert_eq!(check_encode_slice(&v[..]), s);
|
assert_eq!(check_encode_slice(&v[..]), s);
|
||||||
assert_eq!(from_check(&check_encode_slice(&v[..])).ok(), Some(v));
|
assert_eq!(from_check(&check_encode_slice(&v[..])).ok(), Some(v));
|
||||||
|
|
||||||
|
// Check that empty slice passes roundtrip.
|
||||||
|
assert_eq!(from_check(&check_encode_slice(&[])), Ok(vec![]));
|
||||||
|
// Check that `len > 4` is enforced.
|
||||||
|
assert_eq!(from_check(&encode_slice(&[1,2,3])), Err(Error::TooShort(3)));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue