Add Target and Difficulty types

Currently we use the `Uint256` type to represent two proof of work
integers, namely target and difficulty (work).

It would be nice to not have a public integer type that is not fully
implemented (i.e., does not implement arithmetic etc as do integer types
in stdlib). Instead of implementing all the stdlib functions we can
instead add two new wrapper types, since these are not general purpose
integers they do not need to implement anything we do not need to use.

- Add a `pow` module.
- Put a modified version of `Uint256` to `pow`.
- Add two new wrapper types `Target` and `Difficulty`.
- Only implement methods that we use on each type.

Note this patch does not remove the original `Uint256`, that will be
done as a separate patch.
This commit is contained in:
Tobin C. Harding 2022-08-16 15:07:59 +10:00
parent 7228d1fc14
commit cb9893c4a9
12 changed files with 1588 additions and 1124 deletions

View File

@ -10,7 +10,7 @@ jobs:
strategy:
fail-fast: false
matrix:
fuzz_target: [deser_net_msg, deserialize_address, deserialize_amount, deserialize_block, deserialize_psbt, deserialize_script, deserialize_transaction, deserialize_prefilled_transaction, deserialize_witness, outpoint_string, uint128_fuzz, script_bytes_to_asm_fmt]
fuzz_target: [deser_net_msg, deserialize_address, deserialize_amount, deserialize_block, deserialize_psbt, deserialize_script, deserialize_transaction, deserialize_prefilled_transaction, deserialize_witness, outpoint_string, script_bytes_to_asm_fmt]
steps:
- name: Install test dependencies
run: sudo apt-get update -y && sudo apt-get install -y binutils-dev libunwind8-dev libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc libiberty-dev

View File

@ -18,12 +18,11 @@ use crate::util::Error::{BlockBadTarget, BlockBadProofOfWork};
use crate::util::hash::bitcoin_merkle_root;
use crate::hashes::{Hash, HashEngine};
use crate::hash_types::{Wtxid, BlockHash, TxMerkleNode, WitnessMerkleNode, WitnessCommitment};
use crate::util::uint::Uint256;
use crate::consensus::{encode, Encodable, Decodable};
use crate::network::constants::Network;
use crate::blockdata::transaction::Transaction;
use crate::blockdata::constants::{max_target, WITNESS_SCALE_FACTOR};
use crate::blockdata::constants::WITNESS_SCALE_FACTOR;
use crate::blockdata::script;
use crate::pow::{CompactTarget, Target, Work};
use crate::VarInt;
use crate::internal_macros::impl_consensus_encoding;
use crate::io;
@ -50,9 +49,8 @@ pub struct BlockHeader {
pub merkle_root: TxMerkleNode,
/// The timestamp of the block, as claimed by the miner.
pub time: u32,
/// The target value below which the blockhash must lie, encoded as a
/// a float (with well-defined rounding, of course).
pub bits: u32,
/// The target value below which the blockhash must lie.
pub bits: CompactTarget,
/// The nonce, selected to obtain a low enough blockhash.
pub nonce: u32,
}
@ -67,91 +65,33 @@ impl BlockHeader {
BlockHash::from_engine(engine)
}
/// Computes the target [0, T] that a blockhash must land in to be valid.
pub fn target(&self) -> Uint256 {
Self::u256_from_compact_target(self.bits)
}
/// Computes the target value in [`Uint256`] format, from a compact representation.
///
/// [`Uint256`]: ../../util/uint/struct.Uint256.html
///
/// ```
/// use bitcoin::block::BlockHeader;
///
/// assert_eq!(0x1d00ffff,
/// BlockHeader::compact_target_from_u256(
/// &BlockHeader::u256_from_compact_target(0x1d00ffff)
/// )
/// );
/// ```
pub fn u256_from_compact_target(bits: u32) -> Uint256 {
// This is a floating-point "compact" encoding originally used by
// OpenSSL, which satoshi put into consensus code, so we're stuck
// with it. The exponent needs to have 3 subtracted from it, hence
// this goofy decoding code:
let (mant, expt) = {
let unshifted_expt = bits >> 24;
if unshifted_expt <= 3 {
((bits & 0xFFFFFF) >> (8 * (3 - unshifted_expt as usize)), 0)
} else {
(bits & 0xFFFFFF, 8 * ((bits >> 24) - 3))
}
};
// The mantissa is signed but may not be negative
if mant > 0x7FFFFF {
Default::default()
} else {
Uint256::from_u64(mant as u64).unwrap() << (expt as usize)
}
}
/// Computes the target value in float format from Uint256 format.
pub fn compact_target_from_u256(value: &Uint256) -> u32 {
let mut size = (value.bits() + 7) / 8;
let mut compact = if size <= 3 {
(value.low_u64() << (8 * (3 - size))) as u32
} else {
let bn = *value >> (8 * (size - 3));
bn.low_u32()
};
if (compact & 0x00800000) != 0 {
compact >>= 8;
size += 1;
}
compact | (size << 24) as u32
/// Computes the target (range [0, T] inclusive) that a blockhash must land in to be valid.
pub fn target(&self) -> Target {
self.bits.into()
}
/// Computes the popular "difficulty" measure for mining.
pub fn difficulty(&self, network: Network) -> u64 {
(max_target(network) / self.target()).low_u64()
pub fn difficulty(&self) -> u128 {
self.target().difficulty()
}
/// Checks that the proof-of-work for the block is valid, returning the block hash.
pub fn validate_pow(&self, required_target: &Uint256) -> Result<BlockHash, util::Error> {
let target = &self.target();
pub fn validate_pow(&self, required_target: Target) -> Result<BlockHash, util::Error> {
let target = self.target();
if target != required_target {
return Err(BlockBadTarget);
}
let block_hash = self.block_hash();
let mut ret = [0u64; 4];
util::endian::bytes_to_u64_slice_le(block_hash.as_inner(), &mut ret);
let hash = &Uint256(ret);
if hash <= target { Ok(block_hash) } else { Err(BlockBadProofOfWork) }
if target.is_met_by(block_hash) {
Ok(block_hash)
} else {
Err(BlockBadProofOfWork)
}
}
/// Returns the total work of the block.
pub fn work(&self) -> Uint256 {
// 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind)
let mut ret = !self.target();
let mut ret1 = self.target();
ret1.increment();
ret = ret / ret1;
ret.increment();
ret
pub fn work(&self) -> Work {
self.target().to_work()
}
}
@ -451,13 +391,13 @@ impl From<&Block> for BlockHash {
#[cfg(test)]
mod tests {
use super::*;
use crate::hashes::hex::FromHex;
use crate::blockdata::block::{Block, BlockHeader, BlockVersion};
use crate::consensus::encode::{deserialize, serialize};
use crate::util::uint::Uint256;
use crate::util::Error::{BlockBadTarget, BlockBadProofOfWork};
use crate::network::constants::Network;
#[test]
fn test_coinbase_and_bip34() {
@ -487,7 +427,7 @@ mod tests {
let prevhash = Vec::from_hex("4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000").unwrap();
let merkle = Vec::from_hex("bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c").unwrap();
let work = Uint256([0x100010001u64, 0, 0, 0]);
let work = Work::from(0x100010001_u128);
let decode: Result<Block, _> = deserialize(&some_block);
let bad_decode: Result<Block, _> = deserialize(&cutoff_block);
@ -500,11 +440,11 @@ mod tests {
assert_eq!(real_decode.header.merkle_root, real_decode.compute_merkle_root().unwrap());
assert_eq!(serialize(&real_decode.header.merkle_root), merkle);
assert_eq!(real_decode.header.time, 1231965655);
assert_eq!(real_decode.header.bits, 486604799);
assert_eq!(real_decode.header.bits, CompactTarget::from_consensus(486604799));
assert_eq!(real_decode.header.nonce, 2067413810);
assert_eq!(real_decode.header.work(), work);
assert_eq!(real_decode.header.validate_pow(&real_decode.header.target()).unwrap(), real_decode.block_hash());
assert_eq!(real_decode.header.difficulty(Network::Bitcoin), 1);
assert_eq!(real_decode.header.validate_pow(real_decode.header.target()).unwrap(), real_decode.block_hash());
assert_eq!(real_decode.header.difficulty(), 1);
// [test] TODO: check the transaction data
assert_eq!(real_decode.size(), some_block.len());
@ -526,7 +466,7 @@ mod tests {
let prevhash = Vec::from_hex("2aa2f2ca794ccbd40c16e2f3333f6b8b683f9e7179b2c4d74906000000000000").unwrap();
let merkle = Vec::from_hex("10bc26e70a2f672ad420a6153dd0c28b40a6002c55531bfc99bf8994a8e8f67e").unwrap();
let work = Uint256([0x257c3becdacc64u64, 0, 0, 0]);
let work = Work::from(0x257c3becdacc64_u64);
assert!(decode.is_ok());
let real_decode = decode.unwrap();
@ -535,11 +475,11 @@ mod tests {
assert_eq!(serialize(&real_decode.header.merkle_root), merkle);
assert_eq!(real_decode.header.merkle_root, real_decode.compute_merkle_root().unwrap());
assert_eq!(real_decode.header.time, 1472004949);
assert_eq!(real_decode.header.bits, 0x1a06d450);
assert_eq!(real_decode.header.bits, CompactTarget::from_consensus(0x1a06d450));
assert_eq!(real_decode.header.nonce, 1879759182);
assert_eq!(real_decode.header.work(), work);
assert_eq!(real_decode.header.validate_pow(&real_decode.header.target()).unwrap(), real_decode.block_hash());
assert_eq!(real_decode.header.difficulty(Network::Testnet), 2456598);
assert_eq!(real_decode.header.validate_pow(real_decode.header.target()).unwrap(), real_decode.block_hash());
assert_eq!(real_decode.header.difficulty(), 2456598);
// [test] TODO: check the transaction data
assert_eq!(real_decode.size(), segwit_block.len());
@ -570,10 +510,10 @@ mod tests {
fn validate_pow_test() {
let some_header = Vec::from_hex("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b").unwrap();
let some_header: BlockHeader = deserialize(&some_header).expect("Can't deserialize correct block header");
assert_eq!(some_header.validate_pow(&some_header.target()).unwrap(), some_header.block_hash());
assert_eq!(some_header.validate_pow(some_header.target()).unwrap(), some_header.block_hash());
// test with zero target
match some_header.validate_pow(&Uint256::default()) {
match some_header.validate_pow(Target::default()) {
Err(BlockBadTarget) => (),
_ => panic!("unexpected result from validate_pow"),
}
@ -581,7 +521,7 @@ mod tests {
// test with modified header
let mut invalid_header: BlockHeader = some_header;
invalid_header.version.0 += 1;
match invalid_header.validate_pow(&invalid_header.target()) {
match invalid_header.validate_pow(invalid_header.target()) {
Err(BlockBadProofOfWork) => (),
_ => panic!("unexpected result from validate_pow"),
}
@ -593,7 +533,7 @@ mod tests {
let header: BlockHeader = deserialize(&some_header).expect("Can't deserialize correct block header");
assert_eq!(header.bits, BlockHeader::compact_target_from_u256(&header.target()));
assert_eq!(header.bits, header.target().to_compact_lossy());
}
#[test]

View File

@ -21,7 +21,7 @@ use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn, Sequence
use crate::blockdata::block::{Block, BlockHeader, BlockVersion};
use crate::blockdata::witness::Witness;
use crate::network::constants::Network;
use crate::util::uint::Uint256;
use crate::pow::CompactTarget;
use crate::internal_macros::{impl_array_newtype, impl_bytes_newtype};
/// How many satoshis are in "one bitcoin"
@ -55,11 +55,6 @@ pub const SUBSIDY_HALVING_INTERVAL: u32 = 210_000;
/// Maximum allowed value for an integer in Script.
pub const MAX_SCRIPTNUM_VALUE: u32 = 0x80000000; // 2^31
/// In Bitcoind this is insanely described as ~((u256)0 >> 32)
pub fn max_target(_: Network) -> Uint256 {
Uint256::from_u64(0xFFFF).unwrap() << 208
}
/// The maximum value allowed in an output (useful for sanity checking,
/// since keeping everything below this value should prevent overflows
/// if you are doing anything remotely sane with monetary values).
@ -119,7 +114,7 @@ pub fn genesis_block(network: Network) -> Block {
prev_blockhash: Hash::all_zeros(),
merkle_root,
time: 1231006505,
bits: 0x1d00ffff,
bits: CompactTarget::from_consensus(0x1d00ffff),
nonce: 2083236893
},
txdata,
@ -132,7 +127,7 @@ pub fn genesis_block(network: Network) -> Block {
prev_blockhash: Hash::all_zeros(),
merkle_root,
time: 1296688602,
bits: 0x1d00ffff,
bits: CompactTarget::from_consensus(0x1d00ffff),
nonce: 414098458
},
txdata,
@ -145,7 +140,7 @@ pub fn genesis_block(network: Network) -> Block {
prev_blockhash: Hash::all_zeros(),
merkle_root,
time: 1598918400,
bits: 0x1e0377ae,
bits: CompactTarget::from_consensus(0x1e0377ae),
nonce: 52613770
},
txdata,
@ -158,7 +153,7 @@ pub fn genesis_block(network: Network) -> Block {
prev_blockhash: Hash::all_zeros(),
merkle_root,
time: 1296688602,
bits: 0x207fffff,
bits: CompactTarget::from_consensus(0x207fffff),
nonce: 2
},
txdata,
@ -232,7 +227,7 @@ mod test {
assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
assert_eq!(gen.header.time, 1231006505);
assert_eq!(gen.header.bits, 0x1d00ffff);
assert_eq!(gen.header.bits, CompactTarget::from_consensus(0x1d00ffff));
assert_eq!(gen.header.nonce, 2083236893);
assert_eq!(gen.header.block_hash().to_hex(), "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
}
@ -244,7 +239,7 @@ mod test {
assert_eq!(gen.header.prev_blockhash, Hash::all_zeros());
assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
assert_eq!(gen.header.time, 1296688602);
assert_eq!(gen.header.bits, 0x1d00ffff);
assert_eq!(gen.header.bits, CompactTarget::from_consensus(0x1d00ffff));
assert_eq!(gen.header.nonce, 414098458);
assert_eq!(gen.header.block_hash().to_hex(), "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
}
@ -256,7 +251,7 @@ mod test {
assert_eq!(gen.header.prev_blockhash, Hash::all_zeros());
assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
assert_eq!(gen.header.time, 1598918400);
assert_eq!(gen.header.bits, 0x1e0377ae);
assert_eq!(gen.header.bits, CompactTarget::from_consensus(0x1e0377ae));
assert_eq!(gen.header.nonce, 52613770);
assert_eq!(gen.header.block_hash().to_hex(), "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6");
}

View File

@ -8,36 +8,7 @@
//!
use crate::network::constants::Network;
use crate::util::uint::Uint256;
/// Lowest possible difficulty for Mainnet. See comment on Params::pow_limit for more info.
const MAX_BITS_BITCOIN: Uint256 = Uint256([
0x0000000000000000u64,
0x0000000000000000u64,
0x0000000000000000u64,
0x00000000ffff0000u64,
]);
/// Lowest possible difficulty for Testnet. See comment on Params::pow_limit for more info.
const MAX_BITS_TESTNET: Uint256 = Uint256([
0x0000000000000000u64,
0x0000000000000000u64,
0x0000000000000000u64,
0x00000000ffff0000u64,
]);
/// Lowest possible difficulty for Signet. See comment on Params::pow_limit for more info.
const MAX_BITS_SIGNET: Uint256 = Uint256([
0x0000000000000000u64,
0x0000000000000000u64,
0x0000000000000000u64,
0x00000377ae000000u64,
]);
/// Lowest possible difficulty for Regtest. See comment on Params::pow_limit for more info.
const MAX_BITS_REGTEST: Uint256 = Uint256([
0x0000000000000000u64,
0x0000000000000000u64,
0x0000000000000000u64,
0x7fffff0000000000u64,
]);
use crate::pow::Work;
/// Parameters that influence chain consensus.
#[non_exhaustive]
@ -67,7 +38,7 @@ pub struct Params {
/// Still, this should not affect consensus as the only place where the non-compact form of
/// this is used in Bitcoin Core's consensus algorithm is in comparison and there are no
/// compact-expressible values between Bitcoin Core's and the limit expressed here.
pub pow_limit: Uint256,
pub pow_limit: Work,
/// Expected amount of time to mine one block.
pub pow_target_spacing: u64,
/// Difficulty recalculation interval.
@ -90,7 +61,7 @@ impl Params {
bip66_height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
rule_change_activation_threshold: 1916, // 95%
miner_confirmation_window: 2016,
pow_limit: MAX_BITS_BITCOIN,
pow_limit: Work::MAINNET_MIN,
pow_target_spacing: 10 * 60, // 10 minutes.
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
allow_min_difficulty_blocks: false,
@ -104,7 +75,7 @@ impl Params {
bip66_height: 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
rule_change_activation_threshold: 1512, // 75%
miner_confirmation_window: 2016,
pow_limit: MAX_BITS_TESTNET,
pow_limit: Work::TESTNET_MIN,
pow_target_spacing: 10 * 60, // 10 minutes.
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
allow_min_difficulty_blocks: true,
@ -118,7 +89,7 @@ impl Params {
bip66_height: 1,
rule_change_activation_threshold: 1916, // 95%
miner_confirmation_window: 2016,
pow_limit: MAX_BITS_SIGNET,
pow_limit: Work::SIGNET_MIN,
pow_target_spacing: 10 * 60, // 10 minutes.
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
allow_min_difficulty_blocks: false,
@ -132,7 +103,7 @@ impl Params {
bip66_height: 1251, // used only in rpc tests
rule_change_activation_threshold: 108, // 75%
miner_confirmation_window: 144,
pow_limit: MAX_BITS_REGTEST,
pow_limit: Work::REGTEST_MIN,
pow_target_spacing: 10 * 60, // 10 minutes.
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
allow_min_difficulty_blocks: true,

View File

@ -87,6 +87,7 @@ pub mod consensus;
pub mod error;
pub mod hash_types;
pub mod policy;
pub mod pow;
pub mod sign_message;
pub mod util;
@ -106,6 +107,7 @@ pub use crate::blockdata::{constants, opcodes};
pub use crate::consensus::encode::VarInt;
pub use crate::hash_types::*;
pub use crate::network::constants::Network;
pub use crate::pow::{CompactTarget, Target, Work};
pub use crate::util::amount::{Amount, Denomination, SignedAmount};
pub use crate::util::ecdsa::{self, EcdsaSig, EcdsaSigError};
pub use crate::util::key::{KeyPair, PrivateKey, PublicKey, XOnlyPublicKey};

1536
bitcoin/src/pow.rs Normal file

File diff suppressed because it is too large Load Diff

View File

@ -376,8 +376,8 @@ mod test {
use crate::consensus::encode::{deserialize, serialize};
use crate::hashes::hex::FromHex;
use crate::{
Block, BlockHash, BlockHeader, BlockVersion, OutPoint, Script, Sequence, Transaction, TxIn, TxMerkleNode,
TxOut, Txid, Witness,
Block, BlockHash, BlockHeader, BlockVersion, OutPoint, Script, Sequence, Transaction, TxIn,
TxMerkleNode, TxOut, Txid, Witness, CompactTarget,
};
fn dummy_tx(nonce: &[u8]) -> Transaction {
@ -401,7 +401,7 @@ mod test {
prev_blockhash: BlockHash::hash(&[0]),
merkle_root: TxMerkleNode::hash(&[1]),
time: 2,
bits: 3,
bits: CompactTarget::from_consensus(3),
nonce: 4,
},
txdata: vec![dummy_tx(&[2]), dummy_tx(&[3]), dummy_tx(&[4])],

View File

@ -54,9 +54,7 @@ macro_rules! define_le_to_array {
}
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);
@ -89,19 +87,6 @@ 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::*;
@ -109,7 +94,6 @@ mod tests {
#[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);
@ -119,12 +103,4 @@ mod tests {
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]);
}
}

View File

@ -16,7 +16,6 @@ pub mod hash;
pub mod merkleblock;
pub mod psbt;
pub mod taproot;
pub mod uint;
pub mod sighash;
pub(crate) mod endian;
@ -29,27 +28,6 @@ use bitcoin_internals::write_err;
use crate::consensus::encode;
/// 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;
}
/// A general error code, other errors should implement conversions to/from this
/// if appropriate.
#[derive(Debug)]

View File

@ -1,817 +0,0 @@
// Written in 2014 by Andrew Poelstra <apoelstra@wpsoftware.net>
// SPDX-License-Identifier: CC0-1.0
//! 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:literal) => {
/// Little-endian large integer type
#[derive(Copy, Clone, PartialEq, Eq, Hash, Default)]
pub struct $name(pub [u64; $n_words]);
$crate::internal_macros::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 super::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 super::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, "attempted to divide {:#x} by zero", self);
// 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::util::BitArray::one()
}
}
impl core::ops::Mul<$name> for $name {
type Output = $name;
fn mul(self, other: $name) -> $name {
use $crate::util::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::util::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<usize> 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<usize> 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::LowerHex for $name {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
let &$name(ref data) = self;
if f.alternate() {
write!(f, "0x")?;
}
for ch in data.iter().rev() {
write!(f, "{:016x}", ch)?;
}
Ok(())
}
}
impl core::fmt::Debug for $name {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f,"{:#x}", self)
}
}
impl $crate::consensus::Encodable for $name {
#[inline]
fn consensus_encode<W: $crate::io::Write + ?Sized>(
&self,
w: &mut W,
) -> Result<usize, $crate::io::Error> {
let &$name(ref data) = self;
let mut len = 0;
for word in data.iter() {
len += word.consensus_encode(w)?;
}
Ok(len)
}
}
impl $crate::consensus::Decodable for $name {
fn consensus_decode<R: $crate::io::Read + ?Sized>(
r: &mut R,
) -> Result<$name, $crate::consensus::encode::Error> {
use $crate::consensus::Decodable;
let mut ret: [u64; $n_words] = [0; $n_words];
for i in 0..$n_words {
ret[i] = Decodable::consensus_decode(r)?;
}
Ok($name(ret))
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl $crate::serde::Serialize for $name {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: $crate::serde::Serializer,
{
use $crate::hashes::hex::ToHex;
let bytes = self.to_be_bytes();
if serializer.is_human_readable() {
serializer.serialize_str(&bytes.to_hex())
} else {
serializer.serialize_bytes(&bytes)
}
}
}
#[cfg(feature = "serde")]
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
impl<'de> $crate::serde::Deserialize<'de> for $name {
fn deserialize<D: $crate::serde::Deserializer<'de>>(
deserializer: D,
) -> Result<Self, D::Error> {
use core::fmt;
use $crate::hashes::hex::FromHex;
use $crate::serde::de;
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = $name;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} bytes or a hex string with {} characters", $n_words * 8, $n_words * 8 * 2)
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
let bytes = $crate::prelude::Vec::from_hex(s)
.map_err(|_| de::Error::invalid_value(de::Unexpected::Str(s), &self))?;
$name::from_be_slice(&bytes)
.map_err(|_| de::Error::invalid_length(bytes.len() * 2, &self))
}
fn visit_bytes<E>(self, bytes: &[u8]) -> Result<Self::Value, E>
where
E: de::Error,
{
$name::from_be_slice(bytes)
.map_err(|_| de::Error::invalid_length(bytes.len(), &self))
}
}
if deserializer.is_human_readable() {
deserializer.deserialize_str(Visitor)
} else {
deserializer.deserialize_bytes(Visitor)
}
}
}
};
}
construct_uint!(Uint256, 4);
construct_uint!(Uint128, 2);
/// Invalid slice length.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
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)
}
}
crate::error::impl_std_error!(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 crate::consensus::{deserialize, serialize};
use crate::util::uint::{Uint256, Uint128};
use crate::util::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!("{:#x}", Uint256::from_u64(0xDEADBEEF).unwrap()),
"0x00000000000000000000000000000000000000000000000000000000deadbeef");
assert_eq!(format!("{:x}", Uint256::from_u64(0xDEADBEEF).unwrap()),
"00000000000000000000000000000000000000000000000000000000deadbeef");
assert_eq!(format!("{:#x}", Uint256::from_u64(u64::max_value()).unwrap()),
"0x000000000000000000000000000000000000000000000000ffffffffffffffff");
let max_val = Uint256([0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF,
0xFFFFFFFFFFFFFFFF]);
assert_eq!(format!("{:#x}", max_val),
"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
}
#[test]
pub fn uint256_debug_test() {
assert_eq!(format!("{:?}", Uint256::from_u64(0xDEADBEEF).unwrap()),
"0x00000000000000000000000000000000000000000000000000000000deadbeef");
}
#[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!(biggest <= biggest);
assert!(bigger >= big);
assert!(bigger >= small);
assert!(small <= 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 >> 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
assert_eq!(add << 0, Uint256([0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0, 0]));
assert_eq!(add >> 64, Uint256([0xDEADBEEFDEADBEEF, 0, 0, 0]));
assert_eq!(add << 64, Uint256([0, 0xDEADBEEFDEADBEEF, 0xDEADBEEFDEADBEEF, 0]));
}
#[test]
pub fn uint256_serialize_test() {
let start1 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0, 0]);
let start2 = Uint256([0x8C8C3EE70C644118u64, 0x0209E7378231E632, 0xABCD, 0xFFFF]);
let serial1 = serialize(&start1);
let serial2 = serialize(&start2);
let end1: Result<Uint256, _> = deserialize(&serial1);
let end2: Result<Uint256, _> = deserialize(&serial2);
assert_eq!(end1.ok(), Some(start1));
assert_eq!(end2.ok(), Some(start2));
}
#[cfg(feature = "serde")]
#[test]
pub fn uint256_serde_test() {
let check = |uint, hex| {
let json = format!("\"{}\"", hex);
assert_eq!(::serde_json::to_string(&uint).unwrap(), json);
assert_eq!(::serde_json::from_str::<Uint256>(&json).unwrap(), uint);
let bin_encoded = bincode::serialize(&uint).unwrap();
let bin_decoded: Uint256 = bincode::deserialize(&bin_encoded).unwrap();
assert_eq!(bin_decoded, uint);
};
check(
Uint256::from_u64(0).unwrap(),
"0000000000000000000000000000000000000000000000000000000000000000",
);
check(
Uint256::from_u64(0xDEADBEEF).unwrap(),
"00000000000000000000000000000000000000000000000000000000deadbeef",
);
check(
Uint256([0xaa11, 0xbb22, 0xcc33, 0xdd44]),
"000000000000dd44000000000000cc33000000000000bb22000000000000aa11",
);
check(
Uint256([u64::max_value(), u64::max_value(), u64::max_value(), u64::max_value()]),
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
);
check(
Uint256([ 0xA69B4555DEADBEEF, 0xA69B455CD41BB662, 0xD41BB662A69B4550, 0xDEADBEEAA69B455C ]),
"deadbeeaa69b455cd41bb662a69b4550a69b455cd41bb662a69b4555deadbeef",
);
assert!(::serde_json::from_str::<Uint256>("\"fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffg\"").is_err()); // invalid char
assert!(::serde_json::from_str::<Uint256>("\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"").is_err()); // invalid length
assert!(::serde_json::from_str::<Uint256>("\"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\"").is_err()); // invalid length
}
}

View File

@ -56,10 +56,6 @@ path = "fuzz_targets/deserialize_psbt.rs"
name = "deser_net_msg"
path = "fuzz_targets/deser_net_msg.rs"
[[bin]]
name = "uint128_fuzz"
path = "fuzz_targets/uint128_fuzz.rs"
[[bin]]
name = "script_bytes_to_asm_fmt"
path = "fuzz_targets/script_bytes_to_asm_fmt.rs"

View File

@ -1,113 +0,0 @@
extern crate bitcoin;
use std::str::FromStr;
use std::convert::Into;
fn do_test(data: &[u8]) {
macro_rules! read_ints {
($start: expr) => { {
let mut native = 0;
for c in data[$start..$start + 16].iter() {
native <<= 8;
native |= (*c) as u128;
}
// Note BE:
let uint128 = bitcoin::util::uint::Uint128::from(&[native as u64, (native >> 8*8) as u64][..]);
// Checking two conversion methods against each other
let mut slice = [0u8; 16];
slice.copy_from_slice(&data[$start..$start + 16]);
assert_eq!(uint128, bitcoin::util::uint::Uint128::from_be_bytes(slice));
(native, uint128)
} }
}
macro_rules! check_eq {
($native: expr, $uint: expr) => { {
assert_eq!(&[$native as u64, ($native >> 8*8) as u64], $uint.as_bytes());
} }
}
if data.len() != 16*2 { return; }
let (a_native, a) = read_ints!(0);
// Checks using only a:
for i in 0..128 {
check_eq!(a_native << i, a << i);
check_eq!(a_native >> i, a >> i);
}
assert_eq!(a_native as u64, a.low_u64());
assert_eq!(a_native as u32, a.low_u32());
assert_eq!(128 - a_native.leading_zeros() as usize, a.bits());
assert_eq!(a_native as u64, bitcoin::util::uint::Uint128::from_u64(a_native as u64).unwrap().low_u64());
let mut a_inc = a.clone();
a_inc.increment();
check_eq!(a_native.wrapping_add(1), a_inc);
// Checks with two numbers:
let (b_native, b) = read_ints!(16);
check_eq!(a_native.wrapping_add(b_native), a + b);
check_eq!(a_native.wrapping_sub(b_native), a - b);
if b_native != 0 {
check_eq!(a_native.wrapping_div(b_native), a / b);
check_eq!(a_native.wrapping_rem(b_native), a % b);
}
check_eq!(a_native.wrapping_mul(b_native), a * b);
check_eq!(a_native & b_native, a & b);
check_eq!(a_native | b_native, a | b);
check_eq!(a_native ^ b_native, a ^ b);
check_eq!(a_native.wrapping_mul((b_native as u32) as u128), a.mul_u32(b.low_u32()));
assert_eq!(a_native > b_native, a > b);
assert_eq!(a_native >= b_native, a >= b);
assert_eq!(a_native < b_native, a < b);
assert_eq!(a_native <= b_native, a <= b);
}
#[cfg(feature = "afl")]
#[macro_use] extern crate afl;
#[cfg(feature = "afl")]
fn main() {
fuzz!(|data| {
do_test(&data);
});
}
#[cfg(feature = "honggfuzz")]
#[macro_use] extern crate honggfuzz;
#[cfg(feature = "honggfuzz")]
fn main() {
loop {
fuzz!(|data| {
do_test(data);
});
}
}
#[cfg(test)]
mod tests {
fn extend_vec_from_hex(hex: &str, out: &mut Vec<u8>) {
let mut b = 0;
for (idx, c) in hex.as_bytes().iter().enumerate() {
b <<= 4;
match *c {
b'A'..=b'F' => b |= c - b'A' + 10,
b'a'..=b'f' => b |= c - b'a' + 10,
b'0'..=b'9' => b |= c - b'0',
_ => panic!("Bad hex"),
}
if (idx & 1) == 1 {
out.push(b);
b = 0;
}
}
}
#[test]
fn duplicate_crash() {
let mut a = Vec::new();
extend_vec_from_hex("100000a70000000000000000000000000000000000000000000000000000000054", &mut a);
super::do_test(&a);
}
}