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: ACK02a2b43b2b
Kixunil: ACK02a2b43b2b
Tree-SHA512: 4d701756a42b832f03b8d542f3a5278b4ca1d5983ffd7d4630577ebd4cc8f47029719f9018185e01fa459d8fb32426b5cb4d6b8d8b588ebbb7b65e4aeee94412
This commit is contained in:
commit
121db506fa
|
@ -10,7 +10,7 @@ jobs:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
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:
|
steps:
|
||||||
- name: Install test dependencies
|
- 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
|
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
|
||||||
|
|
|
@ -18,12 +18,11 @@ use crate::util::Error::{BlockBadTarget, BlockBadProofOfWork};
|
||||||
use crate::util::hash::bitcoin_merkle_root;
|
use crate::util::hash::bitcoin_merkle_root;
|
||||||
use crate::hashes::{Hash, HashEngine};
|
use crate::hashes::{Hash, HashEngine};
|
||||||
use crate::hash_types::{Wtxid, BlockHash, TxMerkleNode, WitnessMerkleNode, WitnessCommitment};
|
use crate::hash_types::{Wtxid, BlockHash, TxMerkleNode, WitnessMerkleNode, WitnessCommitment};
|
||||||
use crate::util::uint::Uint256;
|
|
||||||
use crate::consensus::{encode, Encodable, Decodable};
|
use crate::consensus::{encode, Encodable, Decodable};
|
||||||
use crate::network::constants::Network;
|
|
||||||
use crate::blockdata::transaction::Transaction;
|
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::blockdata::script;
|
||||||
|
use crate::pow::{CompactTarget, Target, Work};
|
||||||
use crate::VarInt;
|
use crate::VarInt;
|
||||||
use crate::internal_macros::impl_consensus_encoding;
|
use crate::internal_macros::impl_consensus_encoding;
|
||||||
use crate::io;
|
use crate::io;
|
||||||
|
@ -50,9 +49,8 @@ pub struct BlockHeader {
|
||||||
pub merkle_root: TxMerkleNode,
|
pub merkle_root: TxMerkleNode,
|
||||||
/// The timestamp of the block, as claimed by the miner.
|
/// The timestamp of the block, as claimed by the miner.
|
||||||
pub time: u32,
|
pub time: u32,
|
||||||
/// The target value below which the blockhash must lie, encoded as a
|
/// The target value below which the blockhash must lie.
|
||||||
/// a float (with well-defined rounding, of course).
|
pub bits: CompactTarget,
|
||||||
pub bits: u32,
|
|
||||||
/// The nonce, selected to obtain a low enough blockhash.
|
/// The nonce, selected to obtain a low enough blockhash.
|
||||||
pub nonce: u32,
|
pub nonce: u32,
|
||||||
}
|
}
|
||||||
|
@ -67,91 +65,33 @@ impl BlockHeader {
|
||||||
BlockHash::from_engine(engine)
|
BlockHash::from_engine(engine)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Computes the target [0, T] that a blockhash must land in to be valid.
|
/// Computes the target (range [0, T] inclusive) that a blockhash must land in to be valid.
|
||||||
pub fn target(&self) -> Uint256 {
|
pub fn target(&self) -> Target {
|
||||||
Self::u256_from_compact_target(self.bits)
|
self.bits.into()
|
||||||
}
|
|
||||||
|
|
||||||
/// 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 popular "difficulty" measure for mining.
|
/// Computes the popular "difficulty" measure for mining.
|
||||||
pub fn difficulty(&self, network: Network) -> u64 {
|
pub fn difficulty(&self) -> u128 {
|
||||||
(max_target(network) / self.target()).low_u64()
|
self.target().difficulty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that the proof-of-work for the block is valid, returning the block hash.
|
/// 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> {
|
pub fn validate_pow(&self, required_target: Target) -> Result<BlockHash, util::Error> {
|
||||||
let target = &self.target();
|
let target = self.target();
|
||||||
if target != required_target {
|
if target != required_target {
|
||||||
return Err(BlockBadTarget);
|
return Err(BlockBadTarget);
|
||||||
}
|
}
|
||||||
let block_hash = self.block_hash();
|
let block_hash = self.block_hash();
|
||||||
let mut ret = [0u64; 4];
|
if target.is_met_by(block_hash) {
|
||||||
util::endian::bytes_to_u64_slice_le(block_hash.as_inner(), &mut ret);
|
Ok(block_hash)
|
||||||
let hash = &Uint256(ret);
|
} else {
|
||||||
if hash <= target { Ok(block_hash) } else { Err(BlockBadProofOfWork) }
|
Err(BlockBadProofOfWork)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the total work of the block.
|
/// Returns the total work of the block.
|
||||||
pub fn work(&self) -> Uint256 {
|
pub fn work(&self) -> Work {
|
||||||
// 2**256 / (target + 1) == ~target / (target+1) + 1 (eqn shamelessly stolen from bitcoind)
|
self.target().to_work()
|
||||||
let mut ret = !self.target();
|
|
||||||
let mut ret1 = self.target();
|
|
||||||
ret1.increment();
|
|
||||||
ret = ret / ret1;
|
|
||||||
ret.increment();
|
|
||||||
ret
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -451,13 +391,13 @@ impl From<&Block> for BlockHash {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
use crate::hashes::hex::FromHex;
|
use crate::hashes::hex::FromHex;
|
||||||
|
|
||||||
use crate::blockdata::block::{Block, BlockHeader, BlockVersion};
|
use crate::blockdata::block::{Block, BlockHeader, BlockVersion};
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
use crate::consensus::encode::{deserialize, serialize};
|
||||||
use crate::util::uint::Uint256;
|
|
||||||
use crate::util::Error::{BlockBadTarget, BlockBadProofOfWork};
|
use crate::util::Error::{BlockBadTarget, BlockBadProofOfWork};
|
||||||
use crate::network::constants::Network;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_coinbase_and_bip34() {
|
fn test_coinbase_and_bip34() {
|
||||||
|
@ -487,7 +427,7 @@ mod tests {
|
||||||
|
|
||||||
let prevhash = Vec::from_hex("4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000").unwrap();
|
let prevhash = Vec::from_hex("4ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000").unwrap();
|
||||||
let merkle = Vec::from_hex("bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914c").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 decode: Result<Block, _> = deserialize(&some_block);
|
||||||
let bad_decode: Result<Block, _> = deserialize(&cutoff_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!(real_decode.header.merkle_root, real_decode.compute_merkle_root().unwrap());
|
||||||
assert_eq!(serialize(&real_decode.header.merkle_root), merkle);
|
assert_eq!(serialize(&real_decode.header.merkle_root), merkle);
|
||||||
assert_eq!(real_decode.header.time, 1231965655);
|
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.nonce, 2067413810);
|
||||||
assert_eq!(real_decode.header.work(), work);
|
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.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.difficulty(), 1);
|
||||||
// [test] TODO: check the transaction data
|
// [test] TODO: check the transaction data
|
||||||
|
|
||||||
assert_eq!(real_decode.size(), some_block.len());
|
assert_eq!(real_decode.size(), some_block.len());
|
||||||
|
@ -526,7 +466,7 @@ mod tests {
|
||||||
|
|
||||||
let prevhash = Vec::from_hex("2aa2f2ca794ccbd40c16e2f3333f6b8b683f9e7179b2c4d74906000000000000").unwrap();
|
let prevhash = Vec::from_hex("2aa2f2ca794ccbd40c16e2f3333f6b8b683f9e7179b2c4d74906000000000000").unwrap();
|
||||||
let merkle = Vec::from_hex("10bc26e70a2f672ad420a6153dd0c28b40a6002c55531bfc99bf8994a8e8f67e").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());
|
assert!(decode.is_ok());
|
||||||
let real_decode = decode.unwrap();
|
let real_decode = decode.unwrap();
|
||||||
|
@ -535,11 +475,11 @@ mod tests {
|
||||||
assert_eq!(serialize(&real_decode.header.merkle_root), merkle);
|
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.merkle_root, real_decode.compute_merkle_root().unwrap());
|
||||||
assert_eq!(real_decode.header.time, 1472004949);
|
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.nonce, 1879759182);
|
||||||
assert_eq!(real_decode.header.work(), work);
|
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.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.difficulty(), 2456598);
|
||||||
// [test] TODO: check the transaction data
|
// [test] TODO: check the transaction data
|
||||||
|
|
||||||
assert_eq!(real_decode.size(), segwit_block.len());
|
assert_eq!(real_decode.size(), segwit_block.len());
|
||||||
|
@ -570,10 +510,10 @@ mod tests {
|
||||||
fn validate_pow_test() {
|
fn validate_pow_test() {
|
||||||
let some_header = Vec::from_hex("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b").unwrap();
|
let some_header = Vec::from_hex("010000004ddccd549d28f385ab457e98d1b11ce80bfea2c5ab93015ade4973e400000000bf4473e53794beae34e64fccc471dace6ae544180816f89591894e0f417a914cd74d6e49ffff001d323b3a7b").unwrap();
|
||||||
let some_header: BlockHeader = deserialize(&some_header).expect("Can't deserialize correct block header");
|
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
|
// test with zero target
|
||||||
match some_header.validate_pow(&Uint256::default()) {
|
match some_header.validate_pow(Target::ZERO) {
|
||||||
Err(BlockBadTarget) => (),
|
Err(BlockBadTarget) => (),
|
||||||
_ => panic!("unexpected result from validate_pow"),
|
_ => panic!("unexpected result from validate_pow"),
|
||||||
}
|
}
|
||||||
|
@ -581,7 +521,7 @@ mod tests {
|
||||||
// test with modified header
|
// test with modified header
|
||||||
let mut invalid_header: BlockHeader = some_header;
|
let mut invalid_header: BlockHeader = some_header;
|
||||||
invalid_header.version.0 += 1;
|
invalid_header.version.0 += 1;
|
||||||
match invalid_header.validate_pow(&invalid_header.target()) {
|
match invalid_header.validate_pow(invalid_header.target()) {
|
||||||
Err(BlockBadProofOfWork) => (),
|
Err(BlockBadProofOfWork) => (),
|
||||||
_ => panic!("unexpected result from validate_pow"),
|
_ => 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");
|
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]
|
#[test]
|
||||||
|
|
|
@ -21,7 +21,7 @@ use crate::blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn, Sequence
|
||||||
use crate::blockdata::block::{Block, BlockHeader, BlockVersion};
|
use crate::blockdata::block::{Block, BlockHeader, BlockVersion};
|
||||||
use crate::blockdata::witness::Witness;
|
use crate::blockdata::witness::Witness;
|
||||||
use crate::network::constants::Network;
|
use crate::network::constants::Network;
|
||||||
use crate::util::uint::Uint256;
|
use crate::pow::CompactTarget;
|
||||||
use crate::internal_macros::{impl_array_newtype, impl_bytes_newtype};
|
use crate::internal_macros::{impl_array_newtype, impl_bytes_newtype};
|
||||||
|
|
||||||
/// How many satoshis are in "one bitcoin"
|
/// 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.
|
/// Maximum allowed value for an integer in Script.
|
||||||
pub const MAX_SCRIPTNUM_VALUE: u32 = 0x80000000; // 2^31
|
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,
|
/// The maximum value allowed in an output (useful for sanity checking,
|
||||||
/// since keeping everything below this value should prevent overflows
|
/// since keeping everything below this value should prevent overflows
|
||||||
/// if you are doing anything remotely sane with monetary values).
|
/// 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(),
|
prev_blockhash: Hash::all_zeros(),
|
||||||
merkle_root,
|
merkle_root,
|
||||||
time: 1231006505,
|
time: 1231006505,
|
||||||
bits: 0x1d00ffff,
|
bits: CompactTarget::from_consensus(0x1d00ffff),
|
||||||
nonce: 2083236893
|
nonce: 2083236893
|
||||||
},
|
},
|
||||||
txdata,
|
txdata,
|
||||||
|
@ -132,7 +127,7 @@ pub fn genesis_block(network: Network) -> Block {
|
||||||
prev_blockhash: Hash::all_zeros(),
|
prev_blockhash: Hash::all_zeros(),
|
||||||
merkle_root,
|
merkle_root,
|
||||||
time: 1296688602,
|
time: 1296688602,
|
||||||
bits: 0x1d00ffff,
|
bits: CompactTarget::from_consensus(0x1d00ffff),
|
||||||
nonce: 414098458
|
nonce: 414098458
|
||||||
},
|
},
|
||||||
txdata,
|
txdata,
|
||||||
|
@ -145,7 +140,7 @@ pub fn genesis_block(network: Network) -> Block {
|
||||||
prev_blockhash: Hash::all_zeros(),
|
prev_blockhash: Hash::all_zeros(),
|
||||||
merkle_root,
|
merkle_root,
|
||||||
time: 1598918400,
|
time: 1598918400,
|
||||||
bits: 0x1e0377ae,
|
bits: CompactTarget::from_consensus(0x1e0377ae),
|
||||||
nonce: 52613770
|
nonce: 52613770
|
||||||
},
|
},
|
||||||
txdata,
|
txdata,
|
||||||
|
@ -158,7 +153,7 @@ pub fn genesis_block(network: Network) -> Block {
|
||||||
prev_blockhash: Hash::all_zeros(),
|
prev_blockhash: Hash::all_zeros(),
|
||||||
merkle_root,
|
merkle_root,
|
||||||
time: 1296688602,
|
time: 1296688602,
|
||||||
bits: 0x207fffff,
|
bits: CompactTarget::from_consensus(0x207fffff),
|
||||||
nonce: 2
|
nonce: 2
|
||||||
},
|
},
|
||||||
txdata,
|
txdata,
|
||||||
|
@ -232,7 +227,7 @@ mod test {
|
||||||
assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
|
assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
|
||||||
|
|
||||||
assert_eq!(gen.header.time, 1231006505);
|
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.nonce, 2083236893);
|
||||||
assert_eq!(gen.header.block_hash().to_hex(), "000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f");
|
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.prev_blockhash, Hash::all_zeros());
|
||||||
assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
|
assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
|
||||||
assert_eq!(gen.header.time, 1296688602);
|
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.nonce, 414098458);
|
||||||
assert_eq!(gen.header.block_hash().to_hex(), "000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943");
|
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.prev_blockhash, Hash::all_zeros());
|
||||||
assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
|
assert_eq!(gen.header.merkle_root.to_hex(), "4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b");
|
||||||
assert_eq!(gen.header.time, 1598918400);
|
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.nonce, 52613770);
|
||||||
assert_eq!(gen.header.block_hash().to_hex(), "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6");
|
assert_eq!(gen.header.block_hash().to_hex(), "00000008819873e925422c1ff0f99f7cc9bbb232af63a077a480a3633bee1ef6");
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,36 +8,7 @@
|
||||||
//!
|
//!
|
||||||
|
|
||||||
use crate::network::constants::Network;
|
use crate::network::constants::Network;
|
||||||
use crate::util::uint::Uint256;
|
use crate::pow::Work;
|
||||||
|
|
||||||
/// 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,
|
|
||||||
]);
|
|
||||||
|
|
||||||
/// Parameters that influence chain consensus.
|
/// Parameters that influence chain consensus.
|
||||||
#[non_exhaustive]
|
#[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
|
/// 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
|
/// 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.
|
/// 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.
|
/// Expected amount of time to mine one block.
|
||||||
pub pow_target_spacing: u64,
|
pub pow_target_spacing: u64,
|
||||||
/// Difficulty recalculation interval.
|
/// Difficulty recalculation interval.
|
||||||
|
@ -90,7 +61,7 @@ impl Params {
|
||||||
bip66_height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
|
bip66_height: 363725, // 00000000000000000379eaa19dce8c9b722d46ae6a57c2f1a988119488b50931
|
||||||
rule_change_activation_threshold: 1916, // 95%
|
rule_change_activation_threshold: 1916, // 95%
|
||||||
miner_confirmation_window: 2016,
|
miner_confirmation_window: 2016,
|
||||||
pow_limit: MAX_BITS_BITCOIN,
|
pow_limit: Work::MAINNET_MIN,
|
||||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||||
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
|
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
|
||||||
allow_min_difficulty_blocks: false,
|
allow_min_difficulty_blocks: false,
|
||||||
|
@ -104,7 +75,7 @@ impl Params {
|
||||||
bip66_height: 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
|
bip66_height: 330776, // 000000002104c8c45e99a8853285a3b592602a3ccde2b832481da85e9e4ba182
|
||||||
rule_change_activation_threshold: 1512, // 75%
|
rule_change_activation_threshold: 1512, // 75%
|
||||||
miner_confirmation_window: 2016,
|
miner_confirmation_window: 2016,
|
||||||
pow_limit: MAX_BITS_TESTNET,
|
pow_limit: Work::TESTNET_MIN,
|
||||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||||
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
|
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
|
||||||
allow_min_difficulty_blocks: true,
|
allow_min_difficulty_blocks: true,
|
||||||
|
@ -118,7 +89,7 @@ impl Params {
|
||||||
bip66_height: 1,
|
bip66_height: 1,
|
||||||
rule_change_activation_threshold: 1916, // 95%
|
rule_change_activation_threshold: 1916, // 95%
|
||||||
miner_confirmation_window: 2016,
|
miner_confirmation_window: 2016,
|
||||||
pow_limit: MAX_BITS_SIGNET,
|
pow_limit: Work::SIGNET_MIN,
|
||||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||||
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
|
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
|
||||||
allow_min_difficulty_blocks: false,
|
allow_min_difficulty_blocks: false,
|
||||||
|
@ -132,7 +103,7 @@ impl Params {
|
||||||
bip66_height: 1251, // used only in rpc tests
|
bip66_height: 1251, // used only in rpc tests
|
||||||
rule_change_activation_threshold: 108, // 75%
|
rule_change_activation_threshold: 108, // 75%
|
||||||
miner_confirmation_window: 144,
|
miner_confirmation_window: 144,
|
||||||
pow_limit: MAX_BITS_REGTEST,
|
pow_limit: Work::REGTEST_MIN,
|
||||||
pow_target_spacing: 10 * 60, // 10 minutes.
|
pow_target_spacing: 10 * 60, // 10 minutes.
|
||||||
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
|
pow_target_timespan: 14 * 24 * 60 * 60, // 2 weeks.
|
||||||
allow_min_difficulty_blocks: true,
|
allow_min_difficulty_blocks: true,
|
||||||
|
|
|
@ -87,6 +87,7 @@ pub mod consensus;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod hash_types;
|
pub mod hash_types;
|
||||||
pub mod policy;
|
pub mod policy;
|
||||||
|
pub mod pow;
|
||||||
pub mod sign_message;
|
pub mod sign_message;
|
||||||
pub mod util;
|
pub mod util;
|
||||||
|
|
||||||
|
@ -106,6 +107,7 @@ pub use crate::blockdata::{constants, opcodes};
|
||||||
pub use crate::consensus::encode::VarInt;
|
pub use crate::consensus::encode::VarInt;
|
||||||
pub use crate::hash_types::*;
|
pub use crate::hash_types::*;
|
||||||
pub use crate::network::constants::Network;
|
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::amount::{Amount, Denomination, SignedAmount};
|
||||||
pub use crate::util::ecdsa::{self, EcdsaSig, EcdsaSigError};
|
pub use crate::util::ecdsa::{self, EcdsaSig, EcdsaSigError};
|
||||||
pub use crate::util::key::{KeyPair, PrivateKey, PublicKey, XOnlyPublicKey};
|
pub use crate::util::key::{KeyPair, PrivateKey, PublicKey, XOnlyPublicKey};
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -376,8 +376,8 @@ mod test {
|
||||||
use crate::consensus::encode::{deserialize, serialize};
|
use crate::consensus::encode::{deserialize, serialize};
|
||||||
use crate::hashes::hex::FromHex;
|
use crate::hashes::hex::FromHex;
|
||||||
use crate::{
|
use crate::{
|
||||||
Block, BlockHash, BlockHeader, BlockVersion, OutPoint, Script, Sequence, Transaction, TxIn, TxMerkleNode,
|
Block, BlockHash, BlockHeader, BlockVersion, OutPoint, Script, Sequence, Transaction, TxIn,
|
||||||
TxOut, Txid, Witness,
|
TxMerkleNode, TxOut, Txid, Witness, CompactTarget,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn dummy_tx(nonce: &[u8]) -> Transaction {
|
fn dummy_tx(nonce: &[u8]) -> Transaction {
|
||||||
|
@ -401,7 +401,7 @@ mod test {
|
||||||
prev_blockhash: BlockHash::hash(&[0]),
|
prev_blockhash: BlockHash::hash(&[0]),
|
||||||
merkle_root: TxMerkleNode::hash(&[1]),
|
merkle_root: TxMerkleNode::hash(&[1]),
|
||||||
time: 2,
|
time: 2,
|
||||||
bits: 3,
|
bits: CompactTarget::from_consensus(3),
|
||||||
nonce: 4,
|
nonce: 4,
|
||||||
},
|
},
|
||||||
txdata: vec![dummy_tx(&[2]), dummy_tx(&[3]), dummy_tx(&[4])],
|
txdata: vec![dummy_tx(&[2]), dummy_tx(&[3]), dummy_tx(&[4])],
|
||||||
|
|
|
@ -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_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!(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_u16_le, u16);
|
||||||
define_slice_to_le!(slice_to_u32_le, u32);
|
define_slice_to_le!(slice_to_u32_le, u32);
|
||||||
define_slice_to_le!(slice_to_u64_le, u64);
|
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)
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -109,7 +94,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn endianness_test() {
|
fn endianness_test() {
|
||||||
assert_eq!(slice_to_u32_be(&[0xde, 0xad, 0xbe, 0xef]), 0xdeadbeef);
|
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!(u32_to_array_be(0xdeadbeef), [0xde, 0xad, 0xbe, 0xef]);
|
||||||
|
|
||||||
assert_eq!(slice_to_u16_le(&[0xad, 0xde]), 0xdead);
|
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!(u32_to_array_le(0xdeadbeef), [0xef, 0xbe, 0xad, 0xde]);
|
||||||
assert_eq!(u64_to_array_le(0x1badcafedeadbeef), [0xef, 0xbe, 0xad, 0xde, 0xfe, 0xca, 0xad, 0x1b]);
|
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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ pub mod hash;
|
||||||
pub mod merkleblock;
|
pub mod merkleblock;
|
||||||
pub mod psbt;
|
pub mod psbt;
|
||||||
pub mod taproot;
|
pub mod taproot;
|
||||||
pub mod uint;
|
|
||||||
pub mod sighash;
|
pub mod sighash;
|
||||||
|
|
||||||
pub(crate) mod endian;
|
pub(crate) mod endian;
|
||||||
|
@ -29,27 +28,6 @@ use bitcoin_internals::write_err;
|
||||||
|
|
||||||
use crate::consensus::encode;
|
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
|
/// A general error code, other errors should implement conversions to/from this
|
||||||
/// if appropriate.
|
/// if appropriate.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -56,10 +56,6 @@ path = "fuzz_targets/deserialize_psbt.rs"
|
||||||
name = "deser_net_msg"
|
name = "deser_net_msg"
|
||||||
path = "fuzz_targets/deser_net_msg.rs"
|
path = "fuzz_targets/deser_net_msg.rs"
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "uint128_fuzz"
|
|
||||||
path = "fuzz_targets/uint128_fuzz.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "script_bytes_to_asm_fmt"
|
name = "script_bytes_to_asm_fmt"
|
||||||
path = "fuzz_targets/script_bytes_to_asm_fmt.rs"
|
path = "fuzz_targets/script_bytes_to_asm_fmt.rs"
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue