Introduce ToU64 conversion trait
We already explicitly do not support 16 bit machines. Also, because Rust supports `u182`s one cannot infallibly convert from a `usize` to a `u64`. This is unergonomic and results in a ton of casts. We can instead limit our code to running only on machines where `usize` is less that or equal to 64 bits then the infallible conversion is possible. Since 128 bit machines are not a thing yet this does not in reality introduce any limitations on the library. Add a "private" trait to the `internals` crate to do infallible conversion to a `u64` from `usize`. Implement it for all unsigned integers smaller than `u64` as well so we have the option to use the trait instead of `u32::from(foo)`.
This commit is contained in:
parent
5dd20ddfb0
commit
579b76b7cb
|
@ -9,7 +9,7 @@ use core::{convert, fmt, mem};
|
|||
use std::error;
|
||||
|
||||
use hashes::{sha256, siphash24};
|
||||
use internals::impl_array_newtype;
|
||||
use internals::{impl_array_newtype, ToU64 as _};
|
||||
use io::{BufRead, Write};
|
||||
|
||||
use crate::consensus::encode::{self, Decodable, Encodable, VarInt};
|
||||
|
@ -287,7 +287,7 @@ impl Encodable for BlockTransactionsRequest {
|
|||
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
let mut len = self.block_hash.consensus_encode(w)?;
|
||||
// Manually encode indexes because they are differentially encoded VarInts.
|
||||
len += VarInt(self.indexes.len() as u64).consensus_encode(w)?;
|
||||
len += VarInt(self.indexes.len().to_u64()).consensus_encode(w)?;
|
||||
let mut last_idx = 0;
|
||||
for idx in &self.indexes {
|
||||
len += VarInt(*idx - last_idx).consensus_encode(w)?;
|
||||
|
@ -383,7 +383,7 @@ impl BlockTransactions {
|
|||
transactions: {
|
||||
let mut txs = Vec::with_capacity(request.indexes.len());
|
||||
for idx in &request.indexes {
|
||||
if *idx >= block.txdata.len() as u64 {
|
||||
if *idx >= block.txdata.len().to_u64() {
|
||||
return Err(TxIndexOutOfRangeError(*idx));
|
||||
}
|
||||
txs.push(block.txdata[*idx as usize].clone());
|
||||
|
|
|
@ -41,7 +41,7 @@ use core::cmp::{self, Ordering};
|
|||
use core::fmt;
|
||||
|
||||
use hashes::{sha256d, siphash24, HashEngine as _};
|
||||
use internals::write_err;
|
||||
use internals::{write_err, ToU64 as _};
|
||||
use io::{BufRead, Write};
|
||||
|
||||
use crate::block::{Block, BlockHash};
|
||||
|
@ -387,7 +387,7 @@ impl<'a, W: Write> GcsFilterWriter<'a, W> {
|
|||
|
||||
/// Writes the filter to the wrapped writer.
|
||||
pub fn finish(&mut self) -> Result<usize, io::Error> {
|
||||
let nm = self.elements.len() as u64 * self.m;
|
||||
let nm = self.elements.len().to_u64() * self.m;
|
||||
|
||||
// map hashes to [0, n_elements * M)
|
||||
let mut mapped: Vec<_> = self
|
||||
|
|
|
@ -480,6 +480,7 @@ impl std::error::Error for ValidationError {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use hex::{test_hex_unwrap as hex, FromHex};
|
||||
use internals::ToU64 as _;
|
||||
|
||||
use super::*;
|
||||
use crate::consensus::encode::{deserialize, serialize};
|
||||
|
@ -540,7 +541,7 @@ mod tests {
|
|||
assert_eq!(real_decode.base_size(), some_block.len());
|
||||
assert_eq!(
|
||||
real_decode.weight(),
|
||||
Weight::from_non_witness_data_size(some_block.len() as u64)
|
||||
Weight::from_non_witness_data_size(some_block.len().to_u64())
|
||||
);
|
||||
|
||||
// should be also ok for a non-witness block as commitment is optional in that case
|
||||
|
|
|
@ -25,6 +25,8 @@ pub mod fee_rate {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use internals::ToU64 as _;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
|
@ -41,7 +43,7 @@ pub mod fee_rate {
|
|||
|
||||
let rate = FeeRate::from_sat_per_vb(1).expect("1 sat/byte is valid");
|
||||
|
||||
assert_eq!(rate.fee_vb(tx.vsize() as u64), rate.fee_wu(tx.weight()));
|
||||
assert_eq!(rate.fee_vb(tx.vsize().to_u64()), rate.fee_wu(tx.weight()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,8 @@ use core::ops::{
|
|||
Bound, Index, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive,
|
||||
};
|
||||
|
||||
use internals::ToU64 as _;
|
||||
|
||||
use super::witness_version::WitnessVersion;
|
||||
use super::{
|
||||
bytes_to_asm_fmt, Builder, Instruction, InstructionIndices, Instructions, PushBytes,
|
||||
|
@ -399,11 +401,11 @@ impl Script {
|
|||
} else if self.is_witness_program() {
|
||||
32 + 4 + 1 + (107 / 4) + 4 + // The spend cost copied from Core
|
||||
8 + // The serialized size of the TxOut's amount field
|
||||
self.consensus_encode(&mut sink()).expect("sinks don't error") as u64 // The serialized size of this script_pubkey
|
||||
self.consensus_encode(&mut sink()).expect("sinks don't error").to_u64() // The serialized size of this script_pubkey
|
||||
} else {
|
||||
32 + 4 + 1 + 107 + 4 + // The spend cost copied from Core
|
||||
8 + // The serialized size of the TxOut's amount field
|
||||
self.consensus_encode(&mut sink()).expect("sinks don't error") as u64 // The serialized size of this script_pubkey
|
||||
self.consensus_encode(&mut sink()).expect("sinks don't error").to_u64() // The serialized size of this script_pubkey
|
||||
})
|
||||
.expect("dust_relay_fee or script length should not be absurdly large")
|
||||
/ 1000; // divide by 1000 like in Core to get value as it cancels out DEFAULT_MIN_RELAY_TX_FEE
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
use core::ops::Deref;
|
||||
|
||||
use hex::FromHex;
|
||||
use internals::ToU64 as _;
|
||||
|
||||
use super::{opcode_to_verify, Builder, Instruction, PushBytes, Script};
|
||||
use crate::opcodes::all::*;
|
||||
|
@ -100,7 +101,7 @@ impl ScriptBuf {
|
|||
/// Pushes the slice without reserving
|
||||
fn push_slice_no_opt(&mut self, data: &PushBytes) {
|
||||
// Start with a PUSH opcode
|
||||
match data.len() as u64 {
|
||||
match data.len().to_u64() {
|
||||
n if n < opcodes::Ordinary::OP_PUSHDATA1 as u64 => {
|
||||
self.0.push(n as u8);
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
use core::{cmp, fmt, str};
|
||||
|
||||
use hashes::sha256d;
|
||||
use internals::write_err;
|
||||
use internals::{write_err, ToU64 as _};
|
||||
use io::{BufRead, Write};
|
||||
use primitives::Sequence;
|
||||
use units::parse;
|
||||
|
@ -277,7 +277,7 @@ impl TxIn {
|
|||
/// might increase more than `TxIn::legacy_weight`. This happens when the new input added causes
|
||||
/// the input length `VarInt` to increase its encoding length.
|
||||
pub fn legacy_weight(&self) -> Weight {
|
||||
Weight::from_non_witness_data_size(self.base_size() as u64)
|
||||
Weight::from_non_witness_data_size(self.base_size().to_u64())
|
||||
}
|
||||
|
||||
/// The weight of the TxIn when it's included in a segwit transaction (i.e., a transaction
|
||||
|
@ -292,8 +292,8 @@ impl TxIn {
|
|||
/// - the new input is the first segwit input added - this will add an additional 2WU to the
|
||||
/// transaction weight to take into account the segwit marker
|
||||
pub fn segwit_weight(&self) -> Weight {
|
||||
Weight::from_non_witness_data_size(self.base_size() as u64)
|
||||
+ Weight::from_witness_data_size(self.witness.size() as u64)
|
||||
Weight::from_non_witness_data_size(self.base_size().to_u64())
|
||||
+ Weight::from_witness_data_size(self.witness.size().to_u64())
|
||||
}
|
||||
|
||||
/// Returns the base size of this input.
|
||||
|
@ -359,10 +359,10 @@ impl TxOut {
|
|||
/// # Panics
|
||||
///
|
||||
/// If output size * 4 overflows, this should never happen under normal conditions. Use
|
||||
/// `Weght::from_vb_checked(self.size() as u64)` if you are concerned.
|
||||
/// `Weght::from_vb_checked(self.size().to_u64())` if you are concerned.
|
||||
pub fn weight(&self) -> Weight {
|
||||
// Size is equivalent to virtual size since all bytes of a TxOut are non-witness bytes.
|
||||
Weight::from_vb(self.size() as u64).expect("should never happen under normal conditions")
|
||||
Weight::from_vb(self.size().to_u64()).expect("should never happen under normal conditions")
|
||||
}
|
||||
|
||||
/// Returns the total number of bytes that this output contributes to a transaction.
|
||||
|
@ -1184,7 +1184,7 @@ where
|
|||
let (output_count, output_scripts_size) = output_script_lens.into_iter().fold(
|
||||
(0, 0),
|
||||
|(output_count, total_scripts_size), script_len| {
|
||||
let script_size = script_len + VarInt(script_len as u64).size();
|
||||
let script_size = script_len + VarInt(script_len.to_u64()).size();
|
||||
(output_count + 1, total_scripts_size + script_size)
|
||||
},
|
||||
);
|
||||
|
@ -1376,11 +1376,11 @@ impl InputWeightPrediction {
|
|||
let (count, total_size) =
|
||||
witness_element_lengths.into_iter().fold((0, 0), |(count, total_size), elem_len| {
|
||||
let elem_len = *elem_len.borrow();
|
||||
let elem_size = elem_len + VarInt(elem_len as u64).size();
|
||||
let elem_size = elem_len + VarInt(elem_len.to_u64()).size();
|
||||
(count + 1, total_size + elem_size)
|
||||
});
|
||||
let witness_size = if count > 0 { total_size + VarInt(count as u64).size() } else { 0 };
|
||||
let script_size = input_script_len + VarInt(input_script_len as u64).size();
|
||||
let script_size = input_script_len + VarInt(input_script_len.to_u64()).size();
|
||||
|
||||
InputWeightPrediction { script_size, witness_size }
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ use core::{fmt, mem};
|
|||
|
||||
use hashes::{sha256, sha256d, GeneralHash, Hash};
|
||||
use hex::error::{InvalidCharError, OddLengthStringError};
|
||||
use internals::write_err;
|
||||
use internals::{write_err, ToU64 as _};
|
||||
use io::{BufRead, Cursor, Read, Write};
|
||||
|
||||
use crate::bip152::{PrefilledTransaction, ShortId};
|
||||
|
@ -369,7 +369,7 @@ pub trait Decodable: Sized {
|
|||
/// instead.
|
||||
#[inline]
|
||||
fn consensus_decode<R: BufRead + ?Sized>(reader: &mut R) -> Result<Self, Error> {
|
||||
Self::consensus_decode_from_finite_reader(&mut reader.take(MAX_VEC_SIZE as u64))
|
||||
Self::consensus_decode_from_finite_reader(&mut reader.take(MAX_VEC_SIZE.to_u64()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,7 +458,7 @@ macro_rules! impl_var_int_from {
|
|||
$(
|
||||
/// Creates a `VarInt` from a `usize` by casting the to a `u64`.
|
||||
impl From<$ty> for VarInt {
|
||||
fn from(x: $ty) -> Self { VarInt(x as u64) }
|
||||
fn from(x: $ty) -> Self { VarInt(x.to_u64()) }
|
||||
}
|
||||
)*
|
||||
}
|
||||
|
@ -545,7 +545,7 @@ impl Encodable for String {
|
|||
#[inline]
|
||||
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
let b = self.as_bytes();
|
||||
let vi_len = VarInt(b.len() as u64).consensus_encode(w)?;
|
||||
let vi_len = VarInt(b.len().to_u64()).consensus_encode(w)?;
|
||||
w.emit_slice(b)?;
|
||||
Ok(vi_len + b.len())
|
||||
}
|
||||
|
@ -563,7 +563,7 @@ impl Encodable for Cow<'static, str> {
|
|||
#[inline]
|
||||
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
||||
let b = self.as_bytes();
|
||||
let vi_len = VarInt(b.len() as u64).consensus_encode(w)?;
|
||||
let vi_len = VarInt(b.len().to_u64()).consensus_encode(w)?;
|
||||
w.emit_slice(b)?;
|
||||
Ok(vi_len + b.len())
|
||||
}
|
||||
|
@ -644,7 +644,7 @@ macro_rules! impl_vec {
|
|||
w: &mut W,
|
||||
) -> core::result::Result<usize, io::Error> {
|
||||
let mut len = 0;
|
||||
len += VarInt(self.len() as u64).consensus_encode(w)?;
|
||||
len += VarInt(self.len().to_u64()).consensus_encode(w)?;
|
||||
for c in self.iter() {
|
||||
len += c.consensus_encode(w)?;
|
||||
}
|
||||
|
@ -700,7 +700,7 @@ pub(crate) fn consensus_encode_with_size<W: Write + ?Sized>(
|
|||
data: &[u8],
|
||||
w: &mut W,
|
||||
) -> Result<usize, io::Error> {
|
||||
let vi_len = VarInt(data.len() as u64).consensus_encode(w)?;
|
||||
let vi_len = VarInt(data.len().to_u64()).consensus_encode(w)?;
|
||||
w.emit_slice(data)?;
|
||||
Ok(vi_len + data.len())
|
||||
}
|
||||
|
@ -1202,7 +1202,7 @@ mod tests {
|
|||
T: fmt::Debug,
|
||||
{
|
||||
let rand_io_err = Error::Io(io::Error::new(io::ErrorKind::Other, ""));
|
||||
let varint = VarInt((super::MAX_VEC_SIZE / mem::size_of::<T>()) as u64);
|
||||
let varint = VarInt((super::MAX_VEC_SIZE / mem::size_of::<T>()).to_u64());
|
||||
let err = deserialize::<Vec<T>>(&serialize(&varint)).unwrap_err();
|
||||
assert_eq!(discriminant(&err), discriminant(&rand_io_err));
|
||||
}
|
||||
|
|
|
@ -33,7 +33,9 @@ macro_rules! impl_consensus_encoding {
|
|||
fn consensus_decode<R: $crate::io::BufRead + ?Sized>(
|
||||
r: &mut R,
|
||||
) -> core::result::Result<$thing, $crate::consensus::encode::Error> {
|
||||
let mut r = r.take($crate::consensus::encode::MAX_VEC_SIZE as u64);
|
||||
use internals::ToU64 as _;
|
||||
|
||||
let mut r = r.take($crate::consensus::encode::MAX_VEC_SIZE.to_u64());
|
||||
Ok($thing {
|
||||
$($field: $crate::consensus::Decodable::consensus_decode(&mut r)?),+
|
||||
})
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
use core::fmt;
|
||||
|
||||
use internals::ToU64 as _;
|
||||
use io::{BufRead, Write};
|
||||
|
||||
use self::MerkleBlockError::*;
|
||||
|
@ -245,7 +246,7 @@ impl PartialMerkleTree {
|
|||
return Err(NoTransactions);
|
||||
};
|
||||
// check for excessively high numbers of transactions
|
||||
if self.num_transactions as u64 > Weight::MAX_BLOCK / Weight::MIN_TRANSACTION {
|
||||
if self.num_transactions.to_u64() > Weight::MAX_BLOCK / Weight::MIN_TRANSACTION {
|
||||
return Err(TooManyTransactions);
|
||||
}
|
||||
// there can never be more hashes provided than one for every txid
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
use core::{fmt, iter};
|
||||
|
||||
use hashes::sha256d;
|
||||
use internals::ToU64 as _;
|
||||
use io::{BufRead, Write};
|
||||
|
||||
use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, VarInt};
|
||||
|
@ -426,7 +427,7 @@ impl Decodable for HeaderDeserializationWrapper {
|
|||
|
||||
#[inline]
|
||||
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
||||
Self::consensus_decode_from_finite_reader(&mut r.take(MAX_MSG_SIZE as u64))
|
||||
Self::consensus_decode_from_finite_reader(&mut r.take(MAX_MSG_SIZE.to_u64()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -531,7 +532,7 @@ impl Decodable for RawNetworkMessage {
|
|||
|
||||
#[inline]
|
||||
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
|
||||
Self::consensus_decode_from_finite_reader(&mut r.take(MAX_MSG_SIZE as u64))
|
||||
Self::consensus_decode_from_finite_reader(&mut r.take(MAX_MSG_SIZE.to_u64()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
use internals::ToU64 as _;
|
||||
use io::{BufRead, Cursor, Read};
|
||||
|
||||
use crate::bip32::{ChildNumber, DerivationPath, Fingerprint, Xpub};
|
||||
|
@ -71,7 +72,7 @@ impl Map for Psbt {
|
|||
|
||||
impl Psbt {
|
||||
pub(crate) fn decode_global<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
|
||||
let mut r = r.take(MAX_VEC_SIZE as u64);
|
||||
let mut r = r.take(MAX_VEC_SIZE.to_u64());
|
||||
let mut tx: Option<Transaction> = None;
|
||||
let mut version: Option<u32> = None;
|
||||
let mut unknowns: BTreeMap<raw::Key, Vec<u8>> = Default::default();
|
||||
|
@ -100,7 +101,7 @@ impl Psbt {
|
|||
lock_time: Decodable::consensus_decode(&mut decoder)?,
|
||||
});
|
||||
|
||||
if decoder.position() != vlen as u64 {
|
||||
if decoder.position() != vlen.to_u64() {
|
||||
return Err(Error::PartialDataConsumption);
|
||||
}
|
||||
} else {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
use core::fmt;
|
||||
|
||||
use internals::ToU64 as _;
|
||||
use io::{BufRead, Write};
|
||||
|
||||
use super::serialize::{Deserialize, Serialize};
|
||||
|
@ -80,7 +81,7 @@ impl Key {
|
|||
|
||||
let key_byte_size: u64 = byte_size - 1;
|
||||
|
||||
if key_byte_size > MAX_VEC_SIZE as u64 {
|
||||
if key_byte_size > MAX_VEC_SIZE.to_u64() {
|
||||
return Err(encode::Error::OversizedVectorAllocation {
|
||||
requested: key_byte_size as usize,
|
||||
max: MAX_VEC_SIZE,
|
||||
|
|
|
@ -35,3 +35,28 @@ mod parse;
|
|||
#[cfg(feature = "serde")]
|
||||
#[macro_use]
|
||||
pub mod serde;
|
||||
|
||||
/// A conversion trait for unsigned integer types smaller than or equal to 64-bits.
|
||||
///
|
||||
/// This trait exists because [`usize`] doesn't implement `Into<u64>`. We only support 32 and 64 bit
|
||||
/// architectures because of consensus code so we can infallibly do the conversion.
|
||||
pub trait ToU64 {
|
||||
/// Converts unsigned integer type to a [`u64`].
|
||||
fn to_u64(self) -> u64;
|
||||
}
|
||||
|
||||
macro_rules! impl_to_u64 {
|
||||
($($ty:ident),*) => {
|
||||
$(
|
||||
impl ToU64 for $ty { fn to_u64(self) -> u64 { self.into() } }
|
||||
)*
|
||||
}
|
||||
}
|
||||
impl_to_u64!(u8, u16, u32, u64);
|
||||
|
||||
impl ToU64 for usize {
|
||||
fn to_u64(self) -> u64 {
|
||||
crate::const_assert!(core::mem::size_of::<usize>() <= 8);
|
||||
self as u64
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue