Merge rust-bitcoin/rust-bitcoin#1197: Add `Target` and `Work` types

02a2b43b2b Remove Default impl for Target and Work (Tobin C. Harding)
cb9893c4a9 Add Target and Difficulty types (Tobin C. Harding)

Pull request description:

  Ugh! 1600 lines of green and 1100 of red - my apologies.

  Currently we use the `Uint256` type for proof-of-work calculations. It was observed in #1181 that providing a public 256 bit integer type like this implies that it is a general  purpose integer type. We do not want to provide a general purpose integer type (see the 1000 arithmetic functions on stdlib integer types for why not :)

  Add two new opaque integer types `Target` and `Work`. These are the inverse of each other, both conceptually and mathematically.

  There is a lot of code in this PR, sorry about that. At a high level the PR does:

  - Add a `pow` module.
  - Put a modified version of  `Uint256` in `pow`, making it private.
  - Add two new wrapper types `Target` and `Work` that provide a very limited API specific to their use case. In particular there are methods on each to convert to the other.
  - Only implement methods that we use on each type.

  ### Note

  During development I got mixed up with the word "difficulty", I have discovered this has a very specific meaning in Bitcoin. Please see rustdocs on the `Target::difficulty` function for explanation of this term. For this reason we use the type `Work` defined as the inverse of target, and reserve "difficulty" for the Bitcoin concept.

ACKs for top commit:
  apoelstra:
    ACK 02a2b43b2b
  Kixunil:
    ACK 02a2b43b2b

Tree-SHA512: 4d701756a42b832f03b8d542f3a5278b4ca1d5983ffd7d4630577ebd4cc8f47029719f9018185e01fa459d8fb32426b5cb4d6b8d8b588ebbb7b65e4aeee94412
This commit is contained in:
Andrew Poelstra 2022-10-17 18:09:46 +00:00
commit 121db506fa
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
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::ZERO) {
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);
}
}