diff --git a/common/src/lib.rs b/common/src/lib.rs index 770a707f44..86816712f0 100644 --- a/common/src/lib.rs +++ b/common/src/lib.rs @@ -19,6 +19,10 @@ pub mod address; pub mod chain; pub mod primitives; +pub mod uint; + +pub use uint::{Uint128, Uint256}; + #[cfg(test)] mod tests { #[test] diff --git a/common/src/primitives/compact.rs b/common/src/primitives/compact.rs new file mode 100644 index 0000000000..85ac9038de --- /dev/null +++ b/common/src/primitives/compact.rs @@ -0,0 +1,130 @@ +use crate::uint::Uint256; +use std::ops::Shl; + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)] +pub struct Compact(pub u32); + +impl TryFrom for Uint256 { + type Error = Option; + + // https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/arith_uint256.cpp#L203 + fn try_from(value: Compact) -> Result { + let compact = value.0; + let size = compact >> 24; + let mut word = compact & 0x007FFFFF; + + let value = if size <= 3 { + word >>= 8 * (3 - size); + + Uint256::from_u64(word as u64) + } else { + Uint256::from_u64(word as u64).map(|x| { + let shift = 8 * (size - 3); + x.shl(shift as usize) + }) + }; + + match value { + None => Err(None), + Some(value) => { + if (word != 0 && (compact & 0x00800000) != 0) + || (word != 0 + && ((size > 34) + || (word > 0xFF && size > 33) + || (word > 0xFFFF && size > 32))) + { + return Err(Some(value)); + } + + Ok(value) + } + } + } +} + +// https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/arith_uint256.cpp#L223 +impl From for Compact { + fn from(value: Uint256) -> Self { + let mut size = (value.bits() + 7) / 8; + + let mut compact = if size <= 3 { + value.low_u64() << (8 * (3 - size)) + } else { + let bn = value >> (8 * (size - 3)); + bn.low_u64() + }; + + if (compact & 0x00800000) != 0 { + compact >>= 8; + size += 1; + } + + let x = compact as u32 | (size << 24) as u32; + + Compact(x) + } +} + +#[cfg(test)] +mod tests { + // taken from https://github.com/bitcoin/bitcoin/blob/master/src/test/arith_uint256_tests.cpp#L406 + use super::*; + + fn check_conversion(for_uint256: u32, expected_value: u32) { + let uint256 = { + let compact = Compact(for_uint256); + Uint256::try_from(compact).expect("conversion should not fail from compact to uint256") + }; + + let updated_compact = Compact::from(uint256); + assert_eq!(updated_compact, Compact(expected_value)); + } + + #[test] + fn test_compact_uint256_conversion() { + let u256 = Uint256::from_u64(0x80).expect("it should convert with not problems"); + let compact = Compact::from(u256); + assert_eq!(compact, Compact(0x02008000)); + + // zero values + [ + 0x00123456, 0x01003456, 0x02000056, 0x03000000, 0x04000000, 0x00923456, 0x01803456, + 0x02800056, 0x03800000, 0x04800000, + ] + .into_iter() + .for_each(|x| { + check_conversion(x, 0); + }); + + [ + (0x1d00ffff, 0x1d00ffff), + (0x01123456, 0x01120000), + (0x02123456, 0x02123400), + (0x03123456, 0x03123456), + (0x04123456, 0x04123456), + (0x05009234, 0x05009234), + (0x20123456, 0x20123456), + ] + .into_iter() + .for_each(|(x, y)| { + check_conversion(x, y); + }); + } + + #[test] + fn test_err_conversion() { + fn err_conversion(c: u32) { + match Uint256::try_from(Compact(c)) { + Ok(v) => { + panic!("conversion of {} should fail, not {:?}", c, v); + } + Err(e) => { + assert!(e.is_some()) + } + } + } + + err_conversion(0x04923456); + err_conversion(0x01fedcba); + } +} diff --git a/common/src/primitives/mod.rs b/common/src/primitives/mod.rs index bb3dfe9828..651487b3a7 100644 --- a/common/src/primitives/mod.rs +++ b/common/src/primitives/mod.rs @@ -16,15 +16,18 @@ // Author(s): S. Afach pub mod amount; +pub mod compact; pub mod encoding; pub mod error; pub mod height; pub mod id; pub mod merkle; pub mod time; + pub mod version; pub use amount::Amount; +pub use compact::Compact; pub use encoding::{Bech32Error, DecodedBech32}; pub use height::BlockHeight; pub use id::{DataID, Id, Idable, H256}; diff --git a/common/src/uint/endian.rs b/common/src/uint/endian.rs new file mode 100644 index 0000000000..a2cd603e3e --- /dev/null +++ b/common/src/uint/endian.rs @@ -0,0 +1,157 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// +#![allow(unused)] + +macro_rules! define_slice_to_be { + ($name: ident, $type: ty) => { + #[inline] + pub fn $name(slice: &[u8]) -> $type { + assert_eq!(slice.len(), ::core::mem::size_of::<$type>()); + let mut res = 0; + for i in 0..::core::mem::size_of::<$type>() { + res |= (slice[i] as $type) << (::core::mem::size_of::<$type>() - i - 1) * 8; + } + res + } + }; +} +macro_rules! define_slice_to_le { + ($name: ident, $type: ty) => { + #[inline] + pub fn $name(slice: &[u8]) -> $type { + assert_eq!(slice.len(), ::core::mem::size_of::<$type>()); + let mut res = 0; + for i in 0..::core::mem::size_of::<$type>() { + res |= (slice[i] as $type) << i * 8; + } + res + } + }; +} +macro_rules! define_be_to_array { + ($name: ident, $type: ty, $byte_len: expr) => { + #[inline] + pub fn $name(val: $type) -> [u8; $byte_len] { + debug_assert_eq!(::core::mem::size_of::<$type>(), $byte_len); // size_of isn't a constfn in 1.22 + let mut res = [0; $byte_len]; + for i in 0..$byte_len { + res[i] = ((val >> ($byte_len - i - 1) * 8) & 0xff) as u8; + } + res + } + }; +} +macro_rules! define_le_to_array { + ($name: ident, $type: ty, $byte_len: expr) => { + #[inline] + pub fn $name(val: $type) -> [u8; $byte_len] { + debug_assert_eq!(::core::mem::size_of::<$type>(), $byte_len); // size_of isn't a constfn in 1.22 + let mut res = [0; $byte_len]; + for i in 0..$byte_len { + res[i] = ((val >> i * 8) & 0xff) as u8; + } + res + } + }; +} + +define_slice_to_be!(slice_to_u32_be, u32); +define_slice_to_be!(slice_to_u64_be, u64); +define_be_to_array!(u32_to_array_be, u32, 4); +define_be_to_array!(u64_to_array_be, u64, 8); +define_slice_to_le!(slice_to_u16_le, u16); +define_slice_to_le!(slice_to_u32_le, u32); +define_slice_to_le!(slice_to_u64_le, u64); +define_le_to_array!(u16_to_array_le, u16, 2); +define_le_to_array!(u32_to_array_le, u32, 4); +define_le_to_array!(u64_to_array_le, u64, 8); + +#[inline] +pub fn i16_to_array_le(val: i16) -> [u8; 2] { + u16_to_array_le(val as u16) +} +#[inline] +pub fn slice_to_i16_le(slice: &[u8]) -> i16 { + slice_to_u16_le(slice) as i16 +} +#[inline] +pub fn slice_to_i32_le(slice: &[u8]) -> i32 { + slice_to_u32_le(slice) as i32 +} +#[inline] +pub fn i32_to_array_le(val: i32) -> [u8; 4] { + u32_to_array_le(val as u32) +} +#[inline] +pub fn slice_to_i64_le(slice: &[u8]) -> i64 { + slice_to_u64_le(slice) as i64 +} +#[inline] +pub fn i64_to_array_le(val: i64) -> [u8; 8] { + u64_to_array_le(val as u64) +} + +macro_rules! define_chunk_slice_to_int { + ($name: ident, $type: ty, $converter: ident) => { + #[inline] + pub fn $name(inp: &[u8], outp: &mut [$type]) { + assert_eq!(inp.len(), outp.len() * ::core::mem::size_of::<$type>()); + for (outp_val, data_bytes) in + outp.iter_mut().zip(inp.chunks(::core::mem::size_of::<$type>())) + { + *outp_val = $converter(data_bytes); + } + } + }; +} +define_chunk_slice_to_int!(bytes_to_u64_slice_le, u64, slice_to_u64_le); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn endianness_test() { + assert_eq!(slice_to_u32_be(&[0xde, 0xad, 0xbe, 0xef]), 0xdeadbeef); + assert_eq!( + slice_to_u64_be(&[0xde, 0xad, 0xbe, 0xef, 0x1b, 0xad, 0xca, 0xfe]), + 0xdeadbeef1badcafe + ); + assert_eq!(u32_to_array_be(0xdeadbeef), [0xde, 0xad, 0xbe, 0xef]); + + assert_eq!(slice_to_u16_le(&[0xad, 0xde]), 0xdead); + assert_eq!(slice_to_u32_le(&[0xef, 0xbe, 0xad, 0xde]), 0xdeadbeef); + assert_eq!( + slice_to_u64_le(&[0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b]), + 0x1badcafedeadbeef + ); + assert_eq!(u16_to_array_le(0xdead), [0xad, 0xde]); + assert_eq!(u32_to_array_le(0xdeadbeef), [0xef, 0xbe, 0xad, 0xde]); + assert_eq!( + u64_to_array_le(0x1badcafedeadbeef), + [0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b] + ); + } + + #[test] + fn endian_chunk_test() { + let inp = [ + 0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b, 0xfe, 0xca, 0xad, 0x1b, 0xce, 0xfa, + 0x01, 0x02, + ]; + let mut out = [0; 2]; + bytes_to_u64_slice_le(&inp, &mut out); + assert_eq!(out, [0x1badcafedeadbeef, 0x0201face1badcafe]); + } +} diff --git a/common/src/uint/impls.rs b/common/src/uint/impls.rs new file mode 100644 index 0000000000..d3d56f5481 --- /dev/null +++ b/common/src/uint/impls.rs @@ -0,0 +1,789 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// Modified by +// Carla Yap +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +//! Big unsigned integer types. +//! +//! Implementation of various large-but-fixed sized unsigned integer types. +//! The functions here are designed to be fast. + +macro_rules! construct_uint { + ($name:ident, $n_words:expr) => { + /// Little-endian large integer type + #[derive(Copy, Clone, PartialEq, Eq, Hash, Default)] + pub struct $name(pub [u64; $n_words]); + impl_array_newtype!($name, u64, $n_words); + + impl $name { + /// Conversion to u32 + #[inline] + pub fn low_u32(&self) -> u32 { + let &$name(ref arr) = self; + arr[0] as u32 + } + + /// Conversion to u64 + #[inline] + pub fn low_u64(&self) -> u64 { + let &$name(ref arr) = self; + arr[0] as u64 + } + + /// Return the least number of bits needed to represent the number + #[inline] + pub fn bits(&self) -> usize { + let &$name(ref arr) = self; + for i in 1..$n_words { + if arr[$n_words - i] > 0 { + return (0x40 * ($n_words - i + 1)) + - arr[$n_words - i].leading_zeros() as usize; + } + } + 0x40 - arr[0].leading_zeros() as usize + } + + /// Multiplication by u32 + pub fn mul_u32(self, other: u32) -> $name { + let $name(ref arr) = self; + let mut carry = [0u64; $n_words]; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + let not_last_word = i < $n_words - 1; + let upper = other as u64 * (arr[i] >> 32); + let lower = other as u64 * (arr[i] & 0xFFFFFFFF); + if not_last_word { + carry[i + 1] += upper >> 32; + } + let (sum, overflow) = lower.overflowing_add(upper << 32); + ret[i] = sum; + if overflow && not_last_word { + carry[i + 1] += 1; + } + } + $name(ret) + $name(carry) + } + + /// Create an object from a given unsigned 64-bit integer + #[inline] + pub fn from_u64(init: u64) -> Option<$name> { + let mut ret = [0; $n_words]; + ret[0] = init; + Some($name(ret)) + } + + /// Create an object from a given signed 64-bit integer + #[inline] + pub fn from_i64(init: i64) -> Option<$name> { + if init >= 0 { + $name::from_u64(init as u64) + } else { + None + } + } + + /// Creates big integer value from a byte array using + /// big-endian encoding + pub fn from_be_bytes(bytes: [u8; $n_words * 8]) -> $name { + Self::_from_be_slice(&bytes) + } + + /// Creates big integer value from a byte slice using + /// big-endian encoding + pub fn from_be_slice(bytes: &[u8]) -> Result<$name, ParseLengthError> { + if bytes.len() != $n_words * 8 { + Err(ParseLengthError { + actual: bytes.len(), + expected: $n_words * 8, + }) + } else { + Ok(Self::_from_be_slice(bytes)) + } + } + + fn _from_be_slice(bytes: &[u8]) -> $name { + use crate::uint::endian::slice_to_u64_be; + let mut slice = [0u64; $n_words]; + slice + .iter_mut() + .rev() + .zip(bytes.chunks(8)) + .for_each(|(word, bytes)| *word = slice_to_u64_be(bytes)); + $name(slice) + } + + /// Convert a big integer into a byte array using big-endian encoding + pub fn to_be_bytes(&self) -> [u8; $n_words * 8] { + use crate::uint::endian::u64_to_array_be; + let mut res = [0; $n_words * 8]; + for i in 0..$n_words { + let start = i * 8; + res[start..start + 8] + .copy_from_slice(&u64_to_array_be(self.0[$n_words - (i + 1)])); + } + res + } + + // divmod like operation, returns (quotient, remainder) + #[inline] + fn div_rem(self, other: Self) -> (Self, Self) { + let mut sub_copy = self; + let mut shift_copy = other; + let mut ret = [0u64; $n_words]; + + let my_bits = self.bits(); + let your_bits = other.bits(); + + // Check for division by 0 + assert!(your_bits != 0); + + // Early return in case we are dividing by a larger number than us + if my_bits < your_bits { + return ($name(ret), sub_copy); + } + + // Bitwise long division + let mut shift = my_bits - your_bits; + shift_copy = shift_copy << shift; + loop { + if sub_copy >= shift_copy { + ret[shift / 64] |= 1 << (shift % 64); + sub_copy = sub_copy - shift_copy; + } + shift_copy = shift_copy >> 1; + if shift == 0 { + break; + } + shift -= 1; + } + + ($name(ret), sub_copy) + } + + /// Increment by 1 + #[inline] + pub fn increment(&mut self) { + let &mut $name(ref mut arr) = self; + for i in 0..$n_words { + arr[i] = arr[i].wrapping_add(1); + if arr[i] != 0 { + break; + } + } + } + } + + impl PartialOrd for $name { + #[inline] + fn partial_cmp(&self, other: &$name) -> Option<::core::cmp::Ordering> { + Some(self.cmp(&other)) + } + } + + impl Ord for $name { + #[inline] + fn cmp(&self, other: &$name) -> ::core::cmp::Ordering { + // We need to manually implement ordering because we use little-endian + // and the auto derive is a lexicographic ordering(i.e. memcmp) + // which with numbers is equivalent to big-endian + for i in 0..$n_words { + if self[$n_words - 1 - i] < other[$n_words - 1 - i] { + return ::core::cmp::Ordering::Less; + } + if self[$n_words - 1 - i] > other[$n_words - 1 - i] { + return ::core::cmp::Ordering::Greater; + } + } + ::core::cmp::Ordering::Equal + } + } + + impl ::core::ops::Add<$name> for $name { + type Output = $name; + + fn add(self, other: $name) -> $name { + let $name(ref me) = self; + let $name(ref you) = other; + let mut ret = [0u64; $n_words]; + let mut carry = [0u64; $n_words]; + let mut b_carry = false; + for i in 0..$n_words { + ret[i] = me[i].wrapping_add(you[i]); + if i < $n_words - 1 && ret[i] < me[i] { + carry[i + 1] = 1; + b_carry = true; + } + } + if b_carry { + $name(ret) + $name(carry) + } else { + $name(ret) + } + } + } + + impl ::core::ops::Sub<$name> for $name { + type Output = $name; + + #[inline] + fn sub(self, other: $name) -> $name { + self + !other + $crate::uint::BitArray::one() + } + } + + impl ::core::ops::Mul<$name> for $name { + type Output = $name; + + fn mul(self, other: $name) -> $name { + use $crate::uint::BitArray; + let mut me = $name::zero(); + // TODO: be more efficient about this + for i in 0..(2 * $n_words) { + let to_mul = (other >> (32 * i)).low_u32(); + me = me + (self.mul_u32(to_mul) << (32 * i)); + } + me + } + } + + impl ::core::ops::Div<$name> for $name { + type Output = $name; + + fn div(self, other: $name) -> $name { + self.div_rem(other).0 + } + } + + impl ::core::ops::Rem<$name> for $name { + type Output = $name; + + fn rem(self, other: $name) -> $name { + self.div_rem(other).1 + } + } + + impl $crate::uint::BitArray for $name { + #[inline] + fn bit(&self, index: usize) -> bool { + let &$name(ref arr) = self; + arr[index / 64] & (1 << (index % 64)) != 0 + } + + #[inline] + fn bit_slice(&self, start: usize, end: usize) -> $name { + (*self >> start).mask(end - start) + } + + #[inline] + fn mask(&self, n: usize) -> $name { + let &$name(ref arr) = self; + let mut ret = [0; $n_words]; + for i in 0..$n_words { + if n >= 0x40 * (i + 1) { + ret[i] = arr[i]; + } else { + ret[i] = arr[i] & ((1 << (n - 0x40 * i)) - 1); + break; + } + } + $name(ret) + } + + #[inline] + fn trailing_zeros(&self) -> usize { + let &$name(ref arr) = self; + for i in 0..($n_words - 1) { + if arr[i] > 0 { + return (0x40 * i) + arr[i].trailing_zeros() as usize; + } + } + (0x40 * ($n_words - 1)) + arr[$n_words - 1].trailing_zeros() as usize + } + + fn zero() -> $name { + Default::default() + } + fn one() -> $name { + $name({ + let mut ret = [0; $n_words]; + ret[0] = 1; + ret + }) + } + } + + impl ::core::ops::BitAnd<$name> for $name { + type Output = $name; + + #[inline] + fn bitand(self, other: $name) -> $name { + let $name(ref arr1) = self; + let $name(ref arr2) = other; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + ret[i] = arr1[i] & arr2[i]; + } + $name(ret) + } + } + + impl ::core::ops::BitXor<$name> for $name { + type Output = $name; + + #[inline] + fn bitxor(self, other: $name) -> $name { + let $name(ref arr1) = self; + let $name(ref arr2) = other; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + ret[i] = arr1[i] ^ arr2[i]; + } + $name(ret) + } + } + + impl ::core::ops::BitOr<$name> for $name { + type Output = $name; + + #[inline] + fn bitor(self, other: $name) -> $name { + let $name(ref arr1) = self; + let $name(ref arr2) = other; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + ret[i] = arr1[i] | arr2[i]; + } + $name(ret) + } + } + + impl ::core::ops::Not for $name { + type Output = $name; + + #[inline] + fn not(self) -> $name { + let $name(ref arr) = self; + let mut ret = [0u64; $n_words]; + for i in 0..$n_words { + ret[i] = !arr[i]; + } + $name(ret) + } + } + + impl ::core::ops::Shl for $name { + type Output = $name; + + fn shl(self, shift: usize) -> $name { + let $name(ref original) = self; + let mut ret = [0u64; $n_words]; + let word_shift = shift / 64; + let bit_shift = shift % 64; + for i in 0..$n_words { + // Shift + if bit_shift < 64 && i + word_shift < $n_words { + ret[i + word_shift] += original[i] << bit_shift; + } + // Carry + if bit_shift > 0 && i + word_shift + 1 < $n_words { + ret[i + word_shift + 1] += original[i] >> (64 - bit_shift); + } + } + $name(ret) + } + } + + impl ::core::ops::Shr for $name { + type Output = $name; + + fn shr(self, shift: usize) -> $name { + let $name(ref original) = self; + let mut ret = [0u64; $n_words]; + let word_shift = shift / 64; + let bit_shift = shift % 64; + for i in word_shift..$n_words { + // Shift + ret[i - word_shift] += original[i] >> bit_shift; + // Carry + if bit_shift > 0 && i < $n_words - 1 { + ret[i - word_shift] += original[i + 1] << (64 - bit_shift); + } + } + $name(ret) + } + } + + impl ::core::fmt::Debug for $name { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + let &$name(ref data) = self; + write!(f, "0x")?; + for ch in data.iter().rev() { + write!(f, "{:016x}", ch)?; + } + Ok(()) + } + } + }; +} + +construct_uint!(Uint256, 4); +construct_uint!(Uint128, 2); + +/// Invalid slice length +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +/// Invalid slice length +pub struct ParseLengthError { + /// The length of the slice de-facto + pub actual: usize, + /// The required length of the slice + pub expected: usize, +} + +impl ::core::fmt::Display for ParseLengthError { + fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { + write!( + f, + "Invalid length: got {}, expected {}", + self.actual, self.expected + ) + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl ::std::error::Error for ParseLengthError {} + +impl Uint256 { + /// Decay to a uint128 + #[inline] + pub fn low_128(&self) -> Uint128 { + let &Uint256(data) = self; + Uint128([data[0], data[1]]) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::uint::BitArray; + + #[test] + pub fn uint256_bits_test() { + assert_eq!(Uint256::from_u64(255).unwrap().bits(), 8); + assert_eq!(Uint256::from_u64(256).unwrap().bits(), 9); + assert_eq!(Uint256::from_u64(300).unwrap().bits(), 9); + assert_eq!(Uint256::from_u64(60000).unwrap().bits(), 16); + assert_eq!(Uint256::from_u64(70000).unwrap().bits(), 17); + + // Try to read the following lines out loud quickly + let mut shl = Uint256::from_u64(70000).unwrap(); + shl = shl << 100; + assert_eq!(shl.bits(), 117); + shl = shl << 100; + assert_eq!(shl.bits(), 217); + shl = shl << 100; + assert_eq!(shl.bits(), 0); + + // Bit set check + assert!(!Uint256::from_u64(10).unwrap().bit(0)); + assert!(Uint256::from_u64(10).unwrap().bit(1)); + assert!(!Uint256::from_u64(10).unwrap().bit(2)); + assert!(Uint256::from_u64(10).unwrap().bit(3)); + assert!(!Uint256::from_u64(10).unwrap().bit(4)); + } + + #[test] + pub fn uint256_display_test() { + assert_eq!( + format!("{:?}", Uint256::from_u64(0xDEADBEEF).unwrap()), + "0x00000000000000000000000000000000000000000000000000000000deadbeef" + ); + assert_eq!( + format!("{:?}", Uint256::from_u64(u64::max_value()).unwrap()), + "0x000000000000000000000000000000000000000000000000ffffffffffffffff" + ); + + let max_val = Uint256([ + 0xFFFFFFFFFFFFFFFF, + 0xFFFFFFFFFFFFFFFF, + 0xFFFFFFFFFFFFFFFF, + 0xFFFFFFFFFFFFFFFF, + ]); + assert_eq!( + format!("{:?}", max_val), + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + ); + } + + #[test] + pub fn uint256_comp_test() { + let small = Uint256([10u64, 0, 0, 0]); + let big = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]); + let bigger = Uint256([0x9C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]); + let biggest = Uint256([0x5C8C3EE70C644118u64, 0x0209E7378231E632, 0, 1]); + + assert!(small < big); + assert!(big < bigger); + assert!(bigger < biggest); + assert!(bigger <= biggest); + assert!(bigger >= big); + assert!(bigger >= small); + } + + #[test] + pub fn uint_from_be_bytes() { + assert_eq!( + Uint128::from_be_bytes([ + 0x1b, 0xad, 0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xaf, 0xba, 0xbe, 0x2b, 0xed, + 0xfe, 0xed + ]), + Uint128([0xdeafbabe2bedfeed, 0x1badcafedeadbeef]) + ); + + assert_eq!( + Uint256::from_be_bytes([ + 0x1b, 0xad, 0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xaf, 0xba, 0xbe, 0x2b, 0xed, + 0xfe, 0xed, 0xba, 0xad, 0xf0, 0x0d, 0xde, 0xfa, 0xce, 0xda, 0x11, 0xfe, 0xd2, 0xba, + 0xd1, 0xc0, 0xff, 0xe0 + ]), + Uint256([ + 0x11fed2bad1c0ffe0, + 0xbaadf00ddefaceda, + 0xdeafbabe2bedfeed, + 0x1badcafedeadbeef + ]) + ); + } + + #[test] + pub fn uint_to_be_bytes() { + assert_eq!( + Uint128([0xdeafbabe2bedfeed, 0x1badcafedeadbeef]).to_be_bytes(), + [ + 0x1b, 0xad, 0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xaf, 0xba, 0xbe, 0x2b, 0xed, + 0xfe, 0xed + ] + ); + + assert_eq!( + Uint256([ + 0x11fed2bad1c0ffe0, + 0xbaadf00ddefaceda, + 0xdeafbabe2bedfeed, + 0x1badcafedeadbeef + ]) + .to_be_bytes(), + [ + 0x1b, 0xad, 0xca, 0xfe, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xaf, 0xba, 0xbe, 0x2b, 0xed, + 0xfe, 0xed, 0xba, 0xad, 0xf0, 0x0d, 0xde, 0xfa, 0xce, 0xda, 0x11, 0xfe, 0xd2, 0xba, + 0xd1, 0xc0, 0xff, 0xe0 + ] + ); + } + + #[test] + pub fn uint256_arithmetic_test() { + let init = Uint256::from_u64(0xDEADBEEFDEADBEEF).unwrap(); + let copy = init; + + let add = init + copy; + assert_eq!(add, Uint256([0xBD5B7DDFBD5B7DDEu64, 1, 0, 0])); + // Bitshifts + let shl = add << 88; + assert_eq!(shl, Uint256([0u64, 0xDFBD5B7DDE000000, 0x1BD5B7D, 0])); + let shr = shl >> 40; + assert_eq!( + shr, + Uint256([0x7DDE000000000000u64, 0x0001BD5B7DDFBD5B, 0, 0]) + ); + // Increment + let mut incr = shr; + incr.increment(); + assert_eq!( + incr, + Uint256([0x7DDE000000000001u64, 0x0001BD5B7DDFBD5B, 0, 0]) + ); + // Subtraction + let sub = incr - init; + assert_eq!( + sub, + Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]) + ); + // Multiplication + let mult = sub.mul_u32(300); + assert_eq!( + mult, + Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]) + ); + // Division + assert_eq!( + Uint256::from_u64(105).unwrap() / Uint256::from_u64(5).unwrap(), + Uint256::from_u64(21).unwrap() + ); + let div = mult / Uint256::from_u64(300).unwrap(); + assert_eq!( + div, + Uint256([0x9F30411021524112u64, 0x0001BD5B7DDFBD5A, 0, 0]) + ); + + assert_eq!( + Uint256::from_u64(105).unwrap() % Uint256::from_u64(5).unwrap(), + Uint256::from_u64(0).unwrap() + ); + assert_eq!( + Uint256::from_u64(35498456).unwrap() % Uint256::from_u64(3435).unwrap(), + Uint256::from_u64(1166).unwrap() + ); + let rem_src = mult * Uint256::from_u64(39842).unwrap() + Uint256::from_u64(9054).unwrap(); + assert_eq!( + rem_src % Uint256::from_u64(39842).unwrap(), + Uint256::from_u64(9054).unwrap() + ); + // TODO: bit inversion + } + + #[test] + pub fn mul_u32_test() { + let u64_val = Uint256::from_u64(0xDEADBEEFDEADBEEF).unwrap(); + + let u96_res = u64_val.mul_u32(0xFFFFFFFF); + let u128_res = u96_res.mul_u32(0xFFFFFFFF); + let u160_res = u128_res.mul_u32(0xFFFFFFFF); + let u192_res = u160_res.mul_u32(0xFFFFFFFF); + let u224_res = u192_res.mul_u32(0xFFFFFFFF); + let u256_res = u224_res.mul_u32(0xFFFFFFFF); + + assert_eq!(u96_res, Uint256([0xffffffff21524111u64, 0xDEADBEEE, 0, 0])); + assert_eq!( + u128_res, + Uint256([0x21524111DEADBEEFu64, 0xDEADBEEE21524110, 0, 0]) + ); + assert_eq!( + u160_res, + Uint256([0xBD5B7DDD21524111u64, 0x42A4822200000001, 0xDEADBEED, 0]) + ); + assert_eq!( + u192_res, + Uint256([0x63F6C333DEADBEEFu64, 0xBD5B7DDFBD5B7DDB, 0xDEADBEEC63F6C334, 0]) + ); + assert_eq!( + u224_res, + Uint256([0x7AB6FBBB21524111u64, 0xFFFFFFFBA69B4558, 0x854904485964BAAA, 0xDEADBEEB]) + ); + assert_eq!( + u256_res, + Uint256([ + 0xA69B4555DEADBEEFu64, + 0xA69B455CD41BB662, + 0xD41BB662A69B4550, + 0xDEADBEEAA69B455C + ]) + ); + } + + #[test] + pub fn multiplication_test() { + let u64_val = Uint256::from_u64(0xDEADBEEFDEADBEEF).unwrap(); + + let u128_res = u64_val * u64_val; + + assert_eq!( + u128_res, + Uint256([0x048D1354216DA321u64, 0xC1B1CD13A4D13D46, 0, 0]) + ); + + let u256_res = u128_res * u128_res; + + assert_eq!( + u256_res, + Uint256([ + 0xF4E166AAD40D0A41u64, + 0xF5CF7F3618C2C886u64, + 0x4AFCFF6F0375C608u64, + 0x928D92B4D7F5DF33u64 + ]) + ); + } + + #[test] + pub fn increment_test() { + let mut val = Uint256([ + 0xFFFFFFFFFFFFFFFEu64, + 0xFFFFFFFFFFFFFFFFu64, + 0xFFFFFFFFFFFFFFFFu64, + 0xEFFFFFFFFFFFFFFFu64, + ]); + val.increment(); + assert_eq!( + val, + Uint256([ + 0xFFFFFFFFFFFFFFFFu64, + 0xFFFFFFFFFFFFFFFFu64, + 0xFFFFFFFFFFFFFFFFu64, + 0xEFFFFFFFFFFFFFFFu64, + ]) + ); + val.increment(); + assert_eq!( + val, + Uint256([ + 0x0000000000000000u64, + 0x0000000000000000u64, + 0x0000000000000000u64, + 0xF000000000000000u64, + ]) + ); + + let mut val = Uint256([ + 0xFFFFFFFFFFFFFFFFu64, + 0xFFFFFFFFFFFFFFFFu64, + 0xFFFFFFFFFFFFFFFFu64, + 0xFFFFFFFFFFFFFFFFu64, + ]); + val.increment(); + assert_eq!( + val, + Uint256([ + 0x0000000000000000u64, + 0x0000000000000000u64, + 0x0000000000000000u64, + 0x0000000000000000u64, + ]) + ); + } + + #[test] + pub fn uint256_bitslice_test() { + let init = Uint256::from_u64(0xDEADBEEFDEADBEEF).unwrap(); + let add = init + (init << 64); + assert_eq!(add.bit_slice(64, 128), init); + assert_eq!(add.mask(64), init); + } + + #[test] + pub fn uint256_extreme_bitshift_test() { + // Shifting a u64 by 64 bits gives an undefined value, so make sure that + // we're doing the Right Thing here + let init = Uint256::from_u64(0xDEADBEEFDEADBEEF).unwrap(); + + assert_eq!(init << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0, 0])); + let add = (init << 64) + init; + assert_eq!(add, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0])); + assert_eq!(add >> 64, Uint256([0xDEADBEEFDEADBEEF, 0, 0, 0])); + assert_eq!( + add << 64, + Uint256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0]) + ); + } +} diff --git a/common/src/uint/internal_macros.rs b/common/src/uint/internal_macros.rs new file mode 100644 index 0000000000..6ac51b3c60 --- /dev/null +++ b/common/src/uint/internal_macros.rs @@ -0,0 +1,127 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +/// Implements standard array methods for a given wrapper type +#[macro_export] +macro_rules! impl_array_newtype { + ($thing:ident, $ty:ty, $len:expr) => { + impl $thing { + #[inline] + /// Converts the object to a raw pointer + pub fn as_ptr(&self) -> *const $ty { + let &$thing(ref dat) = self; + dat.as_ptr() + } + + #[inline] + /// Converts the object to a mutable raw pointer + pub fn as_mut_ptr(&mut self) -> *mut $ty { + let &mut $thing(ref mut dat) = self; + dat.as_mut_ptr() + } + + #[inline] + /// Returns the length of the object as an array + pub fn len(&self) -> usize { + $len + } + + #[inline] + /// Returns whether the object, as an array, is empty. Always false. + pub fn is_empty(&self) -> bool { + false + } + + #[inline] + /// Returns the underlying bytes. + pub fn as_bytes(&self) -> &[$ty; $len] { + &self.0 + } + + #[inline] + /// Returns the underlying bytes. + pub fn to_bytes(&self) -> [$ty; $len] { + self.0.clone() + } + + #[inline] + /// Returns the underlying bytes. + pub fn into_bytes(self) -> [$ty; $len] { + self.0 + } + } + + impl<'a> ::core::convert::From<&'a [$ty]> for $thing { + fn from(data: &'a [$ty]) -> $thing { + assert_eq!(data.len(), $len); + let mut ret = [0; $len]; + ret.copy_from_slice(&data[..]); + $thing(ret) + } + } + + impl_index_newtype!($thing, $ty); + }; +} + +/// Implements standard indexing methods for a given wrapper type +#[macro_export] +macro_rules! impl_index_newtype { + ($thing:ident, $ty:ty) => { + impl ::core::ops::Index for $thing { + type Output = $ty; + + #[inline] + fn index(&self, index: usize) -> &$ty { + &self.0[index] + } + } + + impl ::core::ops::Index<::core::ops::Range> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, index: ::core::ops::Range) -> &[$ty] { + &self.0[index] + } + } + + impl ::core::ops::Index<::core::ops::RangeTo> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, index: ::core::ops::RangeTo) -> &[$ty] { + &self.0[index] + } + } + + impl ::core::ops::Index<::core::ops::RangeFrom> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, index: ::core::ops::RangeFrom) -> &[$ty] { + &self.0[index] + } + } + + impl ::core::ops::Index<::core::ops::RangeFull> for $thing { + type Output = [$ty]; + + #[inline] + fn index(&self, _: ::core::ops::RangeFull) -> &[$ty] { + &self.0[..] + } + } + }; +} diff --git a/common/src/uint/mod.rs b/common/src/uint/mod.rs new file mode 100644 index 0000000000..d4768d143e --- /dev/null +++ b/common/src/uint/mod.rs @@ -0,0 +1,43 @@ +// Rust Bitcoin Library +// Written in 2014 by +// Andrew Poelstra +// +// To the extent possible under law, the author(s) have dedicated all +// copyright and related and neighboring rights to this software to +// the public domain worldwide. This software is distributed without +// any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication +// along with this software. +// If not, see . +// + +#[macro_use] +pub(crate) mod internal_macros; + +pub(crate) mod endian; + +mod impls; + +pub use impls::{Uint128, Uint256}; + +/// A trait which allows numbers to act as fixed-size bit arrays +pub trait BitArray { + /// Is bit set? + fn bit(&self, idx: usize) -> bool; + + /// Returns an array which is just the bits from start to end + fn bit_slice(&self, start: usize, end: usize) -> Self; + + /// Bitwise and with `n` ones + fn mask(&self, n: usize) -> Self; + + /// Trailing zeros + fn trailing_zeros(&self) -> usize; + + /// Create all-zeros value + fn zero() -> Self; + + /// Create value representing one + fn one() -> Self; +}