Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
130 changes: 130 additions & 0 deletions common/src/primitives/compact.rs
Original file line number Diff line number Diff line change
@@ -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<Compact> for Uint256 {
type Error = Option<Uint256>;

// https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/arith_uint256.cpp#L203
fn try_from(value: Compact) -> Result<Self, Self::Error> {
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<Uint256> 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);
}
}
3 changes: 3 additions & 0 deletions common/src/primitives/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
157 changes: 157 additions & 0 deletions common/src/uint/endian.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Rust Bitcoin Library
// Written in 2014 by
// Andrew Poelstra <apoelstra@wpsoftware.net>
//
// 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 <http://creativecommons.org/publicdomain/zero/1.0/>.
//
#![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]);
}
}
Loading