Merge rust-bitcoin/rust-bitcoin#2931: Remove `VarInt` and use `ReadExt` and `WriteExt` trait methods instead

18d8b0e469 Replace VarInt type with ReadExt and WriteExt functions (Steven Roose)
003db025c1 Return encoded length from WriteExt::emit_slice (Steven Roose)

Pull request description:

  This the meat and potatoes out of Steven's work in #2133 and also closes #1016

ACKs for top commit:
  apoelstra:
    ACK 18d8b0e469 successfully ran local tests

Tree-SHA512: 2df96c91e0fbfdc87158bde9bbdd9565f67e3f66601697d0e22341416c0cd45dd69d09637393993f350354a44031bead99fd0d2f006b4fc6e7613aedc4b0a832
This commit is contained in:
merge-script 2024-09-30 17:31:00 +00:00
commit dfa8692169
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
15 changed files with 205 additions and 239 deletions

View File

@ -12,7 +12,7 @@ use hashes::{sha256, siphash24};
use internals::{impl_array_newtype, ToU64 as _};
use io::{BufRead, Write};
use crate::consensus::encode::{self, Decodable, Encodable, VarInt};
use crate::consensus::encode::{self, Decodable, Encodable, WriteExt, ReadExt};
use crate::internal_macros::{impl_array_newtype_stringify, impl_consensus_encoding};
use crate::prelude::Vec;
use crate::transaction::TxIdentifier;
@ -76,14 +76,14 @@ impl convert::AsRef<Transaction> for PrefilledTransaction {
impl Encodable for PrefilledTransaction {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
Ok(VarInt::from(self.idx).consensus_encode(w)? + self.tx.consensus_encode(w)?)
Ok(w.emit_compact_size(self.idx)? + self.tx.consensus_encode(w)?)
}
}
impl Decodable for PrefilledTransaction {
#[inline]
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
let idx = VarInt::consensus_decode(r)?.0;
let idx = r.read_compact_size()?;
let idx = u16::try_from(idx)
.map_err(|_| encode::Error::ParseFailed("BIP152 prefilled tx index out of bounds"))?;
let tx = Transaction::consensus_decode(r)?;
@ -289,10 +289,10 @@ 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().to_u64()).consensus_encode(w)?;
len += w.emit_compact_size(self.indexes.len())?;
let mut last_idx = 0;
for idx in &self.indexes {
len += VarInt(*idx - last_idx).consensus_encode(w)?;
len += w.emit_compact_size(*idx - last_idx)?;
last_idx = *idx + 1; // can panic here
}
Ok(len)
@ -305,12 +305,12 @@ impl Decodable for BlockTransactionsRequest {
block_hash: BlockHash::consensus_decode(r)?,
indexes: {
// Manually decode indexes because they are differentially encoded VarInts.
let nb_indexes = VarInt::consensus_decode(r)?.0 as usize;
let nb_indexes = r.read_compact_size()? as usize;
// Since the number of indices ultimately represent transactions,
// we can limit the number of indices to the maximum number of
// transactions that would be allowed in a vector.
let byte_size = (nb_indexes)
let byte_size = nb_indexes
.checked_mul(mem::size_of::<Transaction>())
.ok_or(encode::Error::ParseFailed("invalid length"))?;
if byte_size > encode::MAX_VEC_SIZE {
@ -323,8 +323,8 @@ impl Decodable for BlockTransactionsRequest {
let mut indexes = Vec::with_capacity(nb_indexes);
let mut last_index: u64 = 0;
for _ in 0..nb_indexes {
let differential: VarInt = Decodable::consensus_decode(r)?;
last_index = match last_index.checked_add(differential.0) {
let differential = r.read_compact_size()?;
last_index = match last_index.checked_add(differential) {
Some(i) => i,
None => return Err(encode::Error::ParseFailed("block index overflow")),
};

View File

@ -45,8 +45,7 @@ use internals::{write_err, ToU64 as _};
use io::{BufRead, Write};
use crate::block::{Block, BlockHash};
use crate::consensus::encode::VarInt;
use crate::consensus::{Decodable, Encodable};
use crate::consensus::{ReadExt, WriteExt};
use crate::internal_macros::impl_hashencode;
use crate::prelude::{BTreeSet, Borrow, Vec};
use crate::script::{Script, ScriptExt as _};
@ -286,9 +285,9 @@ impl GcsFilterReader {
I::Item: Borrow<[u8]>,
R: BufRead + ?Sized,
{
let n_elements: VarInt = Decodable::consensus_decode(reader).unwrap_or(VarInt(0));
let n_elements = reader.read_compact_size().unwrap_or(0);
// map hashes to [0, n_elements << grp]
let nm = n_elements.0 * self.m;
let nm = n_elements * self.m;
let mut mapped =
query.map(|e| map_to_range(self.filter.hash(e.borrow()), nm)).collect::<Vec<_>>();
// sort
@ -296,14 +295,14 @@ impl GcsFilterReader {
if mapped.is_empty() {
return Ok(true);
}
if n_elements.0 == 0 {
if n_elements == 0 {
return Ok(false);
}
// find first match in two sorted arrays in one read pass
let mut reader = BitStreamReader::new(reader);
let mut data = self.filter.golomb_rice_decode(&mut reader)?;
let mut remaining = n_elements.0 - 1;
let mut remaining = n_elements - 1;
for p in mapped {
loop {
match data.cmp(&p) {
@ -329,9 +328,9 @@ impl GcsFilterReader {
I::Item: Borrow<[u8]>,
R: BufRead + ?Sized,
{
let n_elements: VarInt = Decodable::consensus_decode(reader).unwrap_or(VarInt(0));
let n_elements = reader.read_compact_size().unwrap_or(0);
// map hashes to [0, n_elements << grp]
let nm = n_elements.0 * self.m;
let nm = n_elements * self.m;
let mut mapped =
query.map(|e| map_to_range(self.filter.hash(e.borrow()), nm)).collect::<Vec<_>>();
// sort
@ -340,14 +339,14 @@ impl GcsFilterReader {
if mapped.is_empty() {
return Ok(true);
}
if n_elements.0 == 0 {
if n_elements == 0 {
return Ok(false);
}
// figure if all mapped are there in one read pass
let mut reader = BitStreamReader::new(reader);
let mut data = self.filter.golomb_rice_decode(&mut reader)?;
let mut remaining = n_elements.0 - 1;
let mut remaining = n_elements - 1;
for p in mapped {
loop {
match data.cmp(&p) {
@ -404,7 +403,7 @@ impl<'a, W: Write> GcsFilterWriter<'a, W> {
mapped.sort_unstable();
// write number of elements as varint
let mut wrote = VarInt::from(mapped.len()).consensus_encode(self.writer)?;
let mut wrote = self.writer.emit_compact_size(mapped.len())?;
// write out deltas of sorted values into a Golomb-Rice coded bit stream
let mut writer = BitStreamWriter::new(self.writer);

View File

@ -10,6 +10,7 @@
use core::fmt;
use hashes::{sha256d, HashEngine};
use internals::compact_size;
use io::{BufRead, Write};
use super::Weight;
@ -21,7 +22,6 @@ use crate::pow::{CompactTarget, Target, Work};
use crate::prelude::Vec;
use crate::script::{self, ScriptExt as _};
use crate::transaction::{Transaction, Wtxid};
use crate::VarInt;
hashes::hash_newtype! {
/// A bitcoin block hash.
@ -330,7 +330,7 @@ impl Block {
fn base_size(&self) -> usize {
let mut size = Header::SIZE;
size += VarInt::from(self.txdata.len()).size();
size += compact_size::encoded_size(self.txdata.len());
size += self.txdata.iter().map(|tx| tx.base_size()).sum::<usize>();
size
@ -343,7 +343,7 @@ impl Block {
pub fn total_size(&self) -> usize {
let mut size = Header::SIZE;
size += VarInt::from(self.txdata.len()).size();
size += compact_size::encoded_size(self.txdata.len());
size += self.txdata.iter().map(|tx| tx.total_size()).sum::<usize>();
size

View File

@ -15,7 +15,7 @@ use core::{cmp, fmt, str};
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use hashes::sha256d;
use internals::{write_err, ToU64 as _};
use internals::{compact_size, write_err, ToU64};
use io::{BufRead, Write};
use primitives::Sequence;
use units::parse;
@ -29,7 +29,7 @@ use crate::script::{Script, ScriptBuf, ScriptExt as _, ScriptExtPriv as _};
#[cfg(doc)]
use crate::sighash::{EcdsaSighashType, TapSighashType};
use crate::witness::Witness;
use crate::{Amount, FeeRate, SignedAmount, VarInt};
use crate::{Amount, FeeRate, SignedAmount};
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
@ -305,7 +305,7 @@ impl TxIn {
pub fn base_size(&self) -> usize {
let mut size = OutPoint::SIZE;
size += VarInt::from(self.script_sig.len()).size();
size += compact_size::encoded_size(self.script_sig.len());
size += self.script_sig.len();
size + Sequence::SIZE
@ -407,7 +407,7 @@ impl<'a> Arbitrary<'a> for TxOut {
/// Returns the total number of bytes that this script pubkey would contribute to a transaction.
fn size_from_script_pubkey(script_pubkey: &Script) -> usize {
let len = script_pubkey.len();
Amount::SIZE + VarInt::from(len).size() + len
Amount::SIZE + compact_size::encoded_size(len) + len
}
/// Bitcoin transaction.
@ -609,10 +609,10 @@ impl Transaction {
pub fn base_size(&self) -> usize {
let mut size: usize = 4; // Serialized length of a u32 for the version number.
size += VarInt::from(self.input.len()).size();
size += compact_size::encoded_size(self.input.len());
size += self.input.iter().map(|input| input.base_size()).sum::<usize>();
size += VarInt::from(self.output.len()).size();
size += compact_size::encoded_size(self.output.len());
size += self.output.iter().map(|output| output.size()).sum::<usize>();
size + absolute::LockTime::SIZE
@ -631,14 +631,14 @@ impl Transaction {
size += 2; // 1 byte for the marker and 1 for the flag.
}
size += VarInt::from(self.input.len()).size();
size += compact_size::encoded_size(self.input.len());
size += self
.input
.iter()
.map(|input| if uses_segwit { input.total_size() } else { input.base_size() })
.sum::<usize>();
size += VarInt::from(self.output.len()).size();
size += compact_size::encoded_size(self.output.len());
size += self.output.iter().map(|output| output.size()).sum::<usize>();
size + absolute::LockTime::SIZE
@ -1169,7 +1169,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.to_u64()).size();
let script_size = script_len + compact_size::encoded_size(script_len);
(output_count + 1, total_scripts_size + script_size)
},
);
@ -1199,8 +1199,8 @@ const fn predict_weight_internal(
// version:
4 +
// count varints:
VarInt(input_count as u64).size() +
VarInt(output_count as u64).size() +
compact_size::encoded_size_const(input_count as u64) +
compact_size::encoded_size_const(output_count as u64) +
output_size +
// lock_time
4;
@ -1240,7 +1240,7 @@ pub const fn predict_weight_from_slices(
i = 0;
while i < output_script_lens.len() {
let script_len = output_script_lens[i];
output_scripts_size += script_len + VarInt(script_len as u64).size();
output_scripts_size += script_len + compact_size::encoded_size_const(script_len as u64);
i += 1;
}
@ -1359,13 +1359,13 @@ impl InputWeightPrediction {
T::Item: Borrow<usize>,
{
let (count, total_size) =
witness_element_lengths.into_iter().fold((0, 0), |(count, total_size), elem_len| {
witness_element_lengths.into_iter().fold((0usize, 0), |(count, total_size), elem_len| {
let elem_len = *elem_len.borrow();
let elem_size = elem_len + VarInt(elem_len.to_u64()).size();
let elem_size = elem_len + compact_size::encoded_size(elem_len);
(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.to_u64()).size();
let witness_size = if count > 0 { total_size + compact_size::encoded_size(count) } else { 0 };
let script_size = input_script_len + compact_size::encoded_size(input_script_len);
InputWeightPrediction { script_size, witness_size }
}
@ -1381,16 +1381,16 @@ impl InputWeightPrediction {
// for loops not supported in const fn
while i < witness_element_lengths.len() {
let elem_len = witness_element_lengths[i];
let elem_size = elem_len + VarInt(elem_len as u64).size();
let elem_size = elem_len + compact_size::encoded_size_const(elem_len as u64);
total_size += elem_size;
i += 1;
}
let witness_size = if !witness_element_lengths.is_empty() {
total_size + VarInt(witness_element_lengths.len() as u64).size()
total_size + compact_size::encoded_size_const(witness_element_lengths.len() as u64)
} else {
0
};
let script_size = input_script_len + VarInt(input_script_len as u64).size();
let script_size = input_script_len + compact_size::encoded_size_const(input_script_len as u64);
InputWeightPrediction { script_size, witness_size }
}

View File

@ -12,14 +12,14 @@ use arbitrary::{Arbitrary, Unstructured};
use internals::compact_size;
use io::{BufRead, Write};
use crate::consensus::encode::{Error, MAX_VEC_SIZE};
use crate::consensus::encode::{Error, ReadExt, MAX_VEC_SIZE};
use crate::consensus::{Decodable, Encodable, WriteExt};
use crate::crypto::ecdsa;
use crate::prelude::Vec;
#[cfg(doc)]
use crate::script::ScriptExt as _;
use crate::taproot::{self, TAPROOT_ANNEX_PREFIX};
use crate::{Script, VarInt};
use crate::Script;
/// The Witness is the data used to unlock bitcoin since the [segwit upgrade].
///
@ -137,7 +137,7 @@ pub struct Iter<'a> {
impl Decodable for Witness {
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
let witness_elements = VarInt::consensus_decode(r)?.0 as usize;
let witness_elements = r.read_compact_size()? as usize;
// Minimum size of witness element is 1 byte, so if the count is
// greater than MAX_VEC_SIZE we must return an error.
if witness_elements > MAX_VEC_SIZE {
@ -159,16 +159,15 @@ impl Decodable for Witness {
let mut content = vec![0u8; cursor + 128];
for i in 0..witness_elements {
let element_size_varint = VarInt::consensus_decode(r)?;
let element_size_varint_len = element_size_varint.size();
let element_size = element_size_varint.0 as usize;
let element_size = r.read_compact_size()? as usize;
let element_size_len = compact_size::encoded_size(element_size);
let required_len = cursor
.checked_add(element_size)
.ok_or(self::Error::OversizedVectorAllocation {
requested: usize::MAX,
max: MAX_VEC_SIZE,
})?
.checked_add(element_size_varint_len)
.checked_add(element_size_len)
.ok_or(self::Error::OversizedVectorAllocation {
requested: usize::MAX,
max: MAX_VEC_SIZE,
@ -186,10 +185,7 @@ impl Decodable for Witness {
encode_cursor(&mut content, 0, i, cursor - witness_index_space);
resize_if_needed(&mut content, required_len);
element_size_varint.consensus_encode(
&mut &mut content[cursor..cursor + element_size_varint_len],
)?;
cursor += element_size_varint_len;
cursor += (&mut content[cursor..cursor + element_size_len]).emit_compact_size(element_size)?;
r.read_exact(&mut content[cursor..cursor + element_size])?;
cursor += element_size;
}
@ -234,13 +230,10 @@ fn resize_if_needed(vec: &mut Vec<u8>, required_len: usize) {
impl Encodable for Witness {
// `self.content` includes the varints so encoding here includes them, as expected.
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
let len = VarInt::from(self.witness_elements);
len.consensus_encode(w)?;
let content_with_indices_len = self.content.len();
let indices_size = self.witness_elements * 4;
let content_len = content_with_indices_len - indices_size;
w.emit_slice(&self.content[..content_len])?;
Ok(content_len + len.size())
Ok(w.emit_compact_size(self.witness_elements)? + w.emit_slice(&self.content[..content_len])?)
}
}

View File

@ -18,7 +18,7 @@ use core::{fmt, mem};
use hashes::{sha256, sha256d, GeneralHash, Hash};
use hex::error::{InvalidCharError, OddLengthStringError};
use internals::{write_err, ToU64 as _};
use internals::{compact_size, write_err, ToU64};
use io::{BufRead, Cursor, Read, Write};
use crate::bip152::{PrefilledTransaction, ShortId};
@ -205,11 +205,16 @@ pub trait WriteExt: Write {
/// Outputs an 8-bit signed integer.
fn emit_i8(&mut self, v: i8) -> Result<(), io::Error>;
/// Outputs a variable sized integer ([`CompactSize`]).
///
/// [`CompactSize`]: <https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer>
fn emit_compact_size(&mut self, v: impl ToU64) -> Result<usize, io::Error>;
/// Outputs a boolean.
fn emit_bool(&mut self, v: bool) -> Result<(), io::Error>;
/// Outputs a byte slice.
fn emit_slice(&mut self, v: &[u8]) -> Result<(), io::Error>;
fn emit_slice(&mut self, v: &[u8]) -> Result<usize, io::Error>;
}
/// Extensions of `Read` to decode data as per Bitcoin consensus.
@ -232,6 +237,11 @@ pub trait ReadExt: Read {
/// Reads an 8-bit signed integer.
fn read_i8(&mut self) -> Result<i8, Error>;
/// Reads a variable sized integer ([`CompactSize`]).
///
/// [`CompactSize`]: <https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer>
fn read_compact_size(&mut self) -> Result<u64, Error>;
/// Reads a boolean.
fn read_bool(&mut self) -> Result<bool, Error>;
@ -274,7 +284,16 @@ impl<W: Write + ?Sized> WriteExt for W {
#[inline]
fn emit_bool(&mut self, v: bool) -> Result<(), io::Error> { self.write_all(&[v as u8]) }
#[inline]
fn emit_slice(&mut self, v: &[u8]) -> Result<(), io::Error> { self.write_all(v) }
fn emit_slice(&mut self, v: &[u8]) -> Result<usize, io::Error> {
self.write_all(v)?;
Ok(v.len())
}
#[inline]
fn emit_compact_size(&mut self, v: impl ToU64) -> Result<usize, io::Error> {
let encoded = compact_size::encode(v.to_u64());
self.emit_slice(&encoded)?;
Ok(encoded.len())
}
}
impl<R: Read + ?Sized> ReadExt for R {
@ -303,6 +322,36 @@ impl<R: Read + ?Sized> ReadExt for R {
fn read_slice(&mut self, slice: &mut [u8]) -> Result<(), Error> {
self.read_exact(slice).map_err(Error::Io)
}
#[inline]
fn read_compact_size(&mut self) -> Result<u64, Error> {
match self.read_u8()? {
0xFF => {
let x = self.read_u64()?;
if x < 0x1_0000_0000 { // I.e., would have fit in a `u32`.
Err(Error::NonMinimalVarInt)
} else {
Ok(x)
}
}
0xFE => {
let x = self.read_u32()?;
if x < 0x1_0000 { // I.e., would have fit in a `u16`.
Err(Error::NonMinimalVarInt)
} else {
Ok(x as u64)
}
}
0xFD => {
let x = self.read_u16()?;
if x < 0xFD { // Could have been encoded as a `u8`.
Err(Error::NonMinimalVarInt)
} else {
Ok(x as u64)
}
}
n => Ok(n as u64),
}
}
}
/// Maximum size, in bytes, of a vector we are allowed to decode.
@ -373,10 +422,6 @@ pub trait Decodable: Sized {
}
}
/// A variable-length unsigned integer.
#[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Debug)]
pub struct VarInt(pub u64);
/// Data and a 4-byte checksum.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct CheckedData {
@ -434,96 +479,21 @@ impl_int_encodable!(i16, read_i16, emit_i16);
impl_int_encodable!(i32, read_i32, emit_i32);
impl_int_encodable!(i64, read_i64, emit_i64);
#[allow(clippy::len_without_is_empty)] // VarInt has no concept of 'is_empty'.
impl VarInt {
/// Returns the number of bytes this varint contributes to a transaction size.
///
/// Returns 1 for 0..=0xFC, 3 for 0xFD..=(2^16-1), 5 for 0x10000..=(2^32-1), and 9 otherwise.
#[inline]
pub const fn size(&self) -> usize {
match self.0 {
0..=0xFC => 1,
0xFD..=0xFFFF => 3,
0x10000..=0xFFFFFFFF => 5,
_ => 9,
}
/// Returns 1 for 0..=0xFC, 3 for 0xFD..=(2^16-1), 5 for 0x10000..=(2^32-1), and 9 otherwise.
#[inline]
pub const fn varint_size_u64(v: u64) -> usize {
match v {
0..=0xFC => 1,
0xFD..=0xFFFF => 3,
0x10000..=0xFFFFFFFF => 5,
_ => 9,
}
}
/// Implements `From<T> for VarInt`.
///
/// `VarInt`s are consensus encoded as `u64`s so we store them as such. Casting from any integer size smaller than or equal to `u64` is always safe and the cast value is correctly handled by `consensus_encode`.
macro_rules! impl_var_int_from {
($($ty:tt),*) => {
$(
/// Creates a `VarInt` from a `usize` by casting the to a `u64`.
impl From<$ty> for VarInt {
fn from(x: $ty) -> Self { VarInt(x.to_u64()) }
}
)*
}
}
impl_var_int_from!(u8, u16, u32, u64, usize);
impl Encodable for VarInt {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
match self.0 {
0..=0xFC => {
(self.0 as u8).consensus_encode(w)?;
Ok(1)
}
0xFD..=0xFFFF => {
w.emit_u8(0xFD)?;
(self.0 as u16).consensus_encode(w)?;
Ok(3)
}
0x10000..=0xFFFFFFFF => {
w.emit_u8(0xFE)?;
(self.0 as u32).consensus_encode(w)?;
Ok(5)
}
_ => {
w.emit_u8(0xFF)?;
self.0.consensus_encode(w)?;
Ok(9)
}
}
}
}
impl Decodable for VarInt {
#[inline]
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
let n = ReadExt::read_u8(r)?;
match n {
0xFF => {
let x = ReadExt::read_u64(r)?;
if x < 0x100000000 {
Err(self::Error::NonMinimalVarInt)
} else {
Ok(VarInt::from(x))
}
}
0xFE => {
let x = ReadExt::read_u32(r)?;
if x < 0x10000 {
Err(self::Error::NonMinimalVarInt)
} else {
Ok(VarInt::from(x))
}
}
0xFD => {
let x = ReadExt::read_u16(r)?;
if x < 0xFD {
Err(self::Error::NonMinimalVarInt)
} else {
Ok(VarInt::from(x))
}
}
n => Ok(VarInt::from(n)),
}
}
/// Returns 1 for 0..=0xFC, 3 for 0xFD..=(2^16-1), 5 for 0x10000..=(2^32-1), and 9 otherwise.
#[inline]
pub fn varint_size(v: impl ToU64) -> usize {
varint_size_u64(v.to_u64())
}
impl Encodable for bool {
@ -544,10 +514,7 @@ impl Decodable for bool {
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().to_u64()).consensus_encode(w)?;
w.emit_slice(b)?;
Ok(vi_len + b.len())
consensus_encode_with_size(self.as_bytes(), w)
}
}
@ -562,10 +529,7 @@ impl Decodable for String {
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().to_u64()).consensus_encode(w)?;
w.emit_slice(b)?;
Ok(vi_len + b.len())
consensus_encode_with_size(self.as_bytes(), w)
}
}
@ -586,8 +550,8 @@ macro_rules! impl_array {
&self,
w: &mut W,
) -> core::result::Result<usize, io::Error> {
w.emit_slice(&self[..])?;
Ok(self.len())
let n = w.emit_slice(&self[..])?;
Ok(n)
}
}
@ -644,7 +608,7 @@ macro_rules! impl_vec {
w: &mut W,
) -> core::result::Result<usize, io::Error> {
let mut len = 0;
len += VarInt(self.len().to_u64()).consensus_encode(w)?;
len += w.emit_compact_size(self.len())?;
for c in self.iter() {
len += c.consensus_encode(w)?;
}
@ -657,7 +621,7 @@ macro_rules! impl_vec {
fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
r: &mut R,
) -> core::result::Result<Self, Error> {
let len = VarInt::consensus_decode_from_finite_reader(r)?.0;
let len = r.read_compact_size()?;
// Do not allocate upfront more items than if the sequence of type
// occupied roughly quarter a block. This should never be the case
// for normal data, but even if that's not true - `push` will just
@ -685,7 +649,6 @@ impl_vec!(TxIn);
impl_vec!(Vec<u8>);
impl_vec!(u64);
impl_vec!(TapLeafHash);
impl_vec!(VarInt);
impl_vec!(ShortId);
impl_vec!(PrefilledTransaction);
@ -700,9 +663,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().to_u64()).consensus_encode(w)?;
w.emit_slice(data)?;
Ok(vi_len + data.len())
Ok(w.emit_compact_size(data.len())? + w.emit_slice(data)?)
}
struct ReadBytesFromFiniteReaderOpts {
@ -745,7 +706,7 @@ impl Encodable for Vec<u8> {
impl Decodable for Vec<u8> {
#[inline]
fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
let len = VarInt::consensus_decode(r)?.0 as usize;
let len = r.read_compact_size()? as usize;
// most real-world vec of bytes data, wouldn't be larger than 128KiB
let opts = ReadBytesFromFiniteReaderOpts { len, chunk_size: 128 * 1024 };
read_bytes_from_finite_reader(r, opts)
@ -779,8 +740,7 @@ impl Encodable for CheckedData {
.expect("network message use u32 as length")
.consensus_encode(w)?;
self.checksum().consensus_encode(w)?;
w.emit_slice(&self.data)?;
Ok(8 + self.data.len())
Ok(8 + w.emit_slice(&self.data)?)
}
}
@ -902,6 +862,8 @@ mod tests {
use core::mem::discriminant;
use super::*;
#[cfg(feature = "std")]
use crate::p2p::{message_blockdata::Inventory, Address};
#[test]
fn serialize_int_test() {
@ -952,46 +914,51 @@ mod tests {
assert_eq!(serialize(&723401728380766730i64), [10u8, 10, 10, 10, 10, 10, 10, 10]);
}
#[test]
fn serialize_varint_test() {
assert_eq!(serialize(&VarInt(10)), [10u8]);
assert_eq!(serialize(&VarInt(0xFC)), [0xFCu8]);
assert_eq!(serialize(&VarInt(0xFD)), [0xFDu8, 0xFD, 0]);
assert_eq!(serialize(&VarInt(0xFFF)), [0xFDu8, 0xFF, 0xF]);
assert_eq!(serialize(&VarInt(0xF0F0F0F)), [0xFEu8, 0xF, 0xF, 0xF, 0xF]);
assert_eq!(
serialize(&VarInt(0xF0F0F0F0F0E0)),
[0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0]
);
assert_eq!(
test_varint_encode(0xFF, &0x100000000_u64.to_le_bytes()).unwrap(),
VarInt(0x100000000)
);
assert_eq!(test_varint_encode(0xFE, &0x10000_u64.to_le_bytes()).unwrap(), VarInt(0x10000));
assert_eq!(test_varint_encode(0xFD, &0xFD_u64.to_le_bytes()).unwrap(), VarInt(0xFD));
// Test that length calc is working correctly
test_varint_len(VarInt(0), 1);
test_varint_len(VarInt(0xFC), 1);
test_varint_len(VarInt(0xFD), 3);
test_varint_len(VarInt(0xFFFF), 3);
test_varint_len(VarInt(0x10000), 5);
test_varint_len(VarInt(0xFFFFFFFF), 5);
test_varint_len(VarInt(0xFFFFFFFF + 1), 9);
test_varint_len(VarInt(u64::MAX), 9);
}
fn test_varint_len(varint: VarInt, expected: usize) {
let mut encoder = vec![];
assert_eq!(varint.consensus_encode(&mut encoder).unwrap(), expected);
assert_eq!(varint.size(), expected);
}
fn test_varint_encode(n: u8, x: &[u8]) -> Result<VarInt, Error> {
fn test_varint_encode(n: u8, x: &[u8]) -> Result<u64, Error> {
let mut input = [0u8; 9];
input[0] = n;
input[1..x.len() + 1].copy_from_slice(x);
deserialize_partial::<VarInt>(&input).map(|t| t.0)
(&input[..]).read_compact_size()
}
#[test]
fn serialize_varint_test() {
fn encode(v: u64) -> Vec<u8> {
let mut buf = Vec::new();
buf.emit_compact_size(v).unwrap();
buf
}
assert_eq!(encode(10), [10u8]);
assert_eq!(encode(0xFC), [0xFCu8]);
assert_eq!(encode(0xFD), [0xFDu8, 0xFD, 0]);
assert_eq!(encode(0xFFF), [0xFDu8, 0xFF, 0xF]);
assert_eq!(encode(0xF0F0F0F), [0xFEu8, 0xF, 0xF, 0xF, 0xF]);
assert_eq!(
encode(0xF0F0F0F0F0E0),
vec![0xFFu8, 0xE0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0, 0],
);
assert_eq!(
test_varint_encode(0xFF, &0x100000000_u64.to_le_bytes()).unwrap(),
0x100000000,
);
assert_eq!(test_varint_encode(0xFE, &0x10000_u64.to_le_bytes()).unwrap(), 0x10000);
assert_eq!(test_varint_encode(0xFD, &0xFD_u64.to_le_bytes()).unwrap(), 0xFD);
// Test that length calc is working correctly
fn test_varint_len(varint: u64, expected: usize) {
let mut encoder = vec![];
assert_eq!(encoder.emit_compact_size(varint).unwrap(), expected);
assert_eq!(varint_size(varint), expected);
}
test_varint_len(0, 1);
test_varint_len(0xFC, 1);
test_varint_len(0xFD, 3);
test_varint_len(0xFFFF, 3);
test_varint_len(0x10000, 5);
test_varint_len(0xFFFFFFFF, 5);
test_varint_len(0xFFFFFFFF + 1, 9);
test_varint_len(u64::MAX, 9);
}
#[test]
@ -1199,8 +1166,9 @@ 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>()).to_u64());
let err = deserialize::<Vec<T>>(&serialize(&varint)).unwrap_err();
let mut buf = Vec::new();
buf.emit_compact_size(super::MAX_VEC_SIZE / mem::size_of::<T>()).unwrap();
let err = deserialize::<Vec<T>>(&buf).unwrap_err();
assert_eq!(discriminant(&err), discriminant(&rand_io_err));
}

View File

@ -131,7 +131,6 @@ pub use crate::{
blockdata::transaction::{self, OutPoint, Transaction, TxIn, TxOut, Txid, Wtxid},
blockdata::weight::Weight,
blockdata::witness::{self, Witness},
consensus::encode::VarInt,
crypto::ecdsa,
crypto::key::{self, PrivateKey, PubkeyHash, PublicKey, CompressedPublicKey, WPubkeyHash, XOnlyPublicKey},
crypto::sighash::{self, LegacySighash, SegwitV0Sighash, TapSighash, TapSighashTag},

View File

@ -16,7 +16,7 @@ use io::{BufRead, Write};
use self::MerkleBlockError::*;
use crate::block::{self, Block};
use crate::consensus::encode::{self, Decodable, Encodable, MAX_VEC_SIZE};
use crate::consensus::encode::{self, Decodable, Encodable, MAX_VEC_SIZE, WriteExt, ReadExt};
use crate::merkle_tree::{MerkleNode as _, TxMerkleNode};
use crate::prelude::Vec;
use crate::transaction::{Transaction, Txid};
@ -405,7 +405,7 @@ impl Encodable for PartialMerkleTree {
ret += self.hashes.consensus_encode(w)?;
let nb_bytes_for_bits = (self.bits.len() + 7) / 8;
ret += encode::VarInt::from(nb_bytes_for_bits).consensus_encode(w)?;
ret += w.emit_compact_size(nb_bytes_for_bits)?;
for chunk in self.bits.chunks(8) {
let mut byte = 0u8;
for (i, bit) in chunk.iter().enumerate() {
@ -424,7 +424,7 @@ impl Decodable for PartialMerkleTree {
let num_transactions: u32 = Decodable::consensus_decode(r)?;
let hashes: Vec<TxMerkleNode> = Decodable::consensus_decode(r)?;
let nb_bytes_for_bits = encode::VarInt::consensus_decode(r)?.0 as usize;
let nb_bytes_for_bits = r.read_compact_size()? as usize;
if nb_bytes_for_bits > MAX_VEC_SIZE {
return Err(encode::Error::OversizedVectorAllocation {
requested: nb_bytes_for_bits,

View File

@ -10,7 +10,7 @@ use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6, ToSoc
use io::{BufRead, Read, Write};
use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, VarInt, WriteExt};
use crate::consensus::encode::{self, Decodable, Encodable, ReadExt, WriteExt};
use crate::p2p::ServiceFlags;
/// A message which can be sent on the Bitcoin network
@ -146,11 +146,7 @@ impl Encodable for AddrV2 {
network: u8,
bytes: &[u8],
) -> Result<usize, io::Error> {
let len = network.consensus_encode(w)?
+ VarInt::from(bytes.len()).consensus_encode(w)?
+ bytes.len();
w.emit_slice(bytes)?;
Ok(len)
Ok(network.consensus_encode(w)? + encode::consensus_encode_with_size(bytes, w)?)
}
Ok(match *self {
AddrV2::Ipv4(ref addr) => encode_addr(w, 1, &addr.octets())?,
@ -167,7 +163,7 @@ impl Encodable for AddrV2 {
impl Decodable for AddrV2 {
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
let network_id = u8::consensus_decode(r)?;
let len = VarInt::consensus_decode(r)?.0;
let len = r.read_compact_size()?;
if len > 512 {
return Err(encode::Error::ParseFailed("IP must be <= 512 bytes"));
}
@ -272,7 +268,7 @@ impl Encodable for AddrV2Message {
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
let mut len = 0;
len += self.time.consensus_encode(w)?;
len += VarInt(self.services.to_u64()).consensus_encode(w)?;
len += w.emit_compact_size(self.services.to_u64())?;
len += self.addr.consensus_encode(w)?;
w.write_all(&self.port.to_be_bytes())?;
@ -286,7 +282,7 @@ impl Decodable for AddrV2Message {
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, encode::Error> {
Ok(AddrV2Message {
time: Decodable::consensus_decode(r)?,
services: ServiceFlags::from(VarInt::consensus_decode(r)?.0),
services: ServiceFlags::from(r.read_compact_size()?),
addr: Decodable::consensus_decode(r)?,
port: u16::swap_bytes(Decodable::consensus_decode(r)?),
})

View File

@ -11,7 +11,7 @@ use hashes::sha256d;
use internals::ToU64 as _;
use io::{BufRead, Write};
use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, VarInt};
use crate::consensus::encode::{self, CheckedData, Decodable, Encodable, WriteExt, ReadExt};
use crate::merkle_tree::MerkleBlock;
use crate::p2p::address::{AddrV2Message, Address};
use crate::p2p::{
@ -337,7 +337,7 @@ impl<'a> Encodable for HeaderSerializationWrapper<'a> {
#[inline]
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
let mut len = 0;
len += VarInt::from(self.0.len()).consensus_encode(w)?;
len += w.emit_compact_size(self.0.len())?;
for header in self.0.iter() {
len += header.consensus_encode(w)?;
len += 0u8.consensus_encode(w)?;
@ -410,7 +410,7 @@ impl Decodable for HeaderDeserializationWrapper {
fn consensus_decode_from_finite_reader<R: BufRead + ?Sized>(
r: &mut R,
) -> Result<Self, encode::Error> {
let len = VarInt::consensus_decode(r)?.0;
let len = r.read_compact_size()?;
// should be above usual number of items to avoid
// allocation
let mut ret = Vec::with_capacity(core::cmp::min(1024 * 16, len as usize));

View File

@ -12,7 +12,7 @@ use io::{BufRead, Write};
use super::serialize::{Deserialize, Serialize};
use crate::consensus::encode::{
self, deserialize, serialize, Decodable, Encodable, ReadExt, VarInt, WriteExt, MAX_VEC_SIZE,
self, deserialize, serialize, Decodable, Encodable, ReadExt, WriteExt, MAX_VEC_SIZE,
};
use crate::prelude::{DisplayHex, Vec};
use crate::psbt::Error;
@ -73,7 +73,7 @@ impl fmt::Display for Key {
impl Key {
pub(crate) fn decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
let VarInt(byte_size): VarInt = Decodable::consensus_decode(r)?;
let byte_size = r.read_compact_size()?;
if byte_size == 0 {
return Err(Error::NoMorePairs);
@ -103,9 +103,7 @@ impl Key {
impl Serialize for Key {
fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::new();
VarInt::from(self.key_data.len() + 1)
.consensus_encode(&mut buf)
.expect("in-memory writers don't error");
buf.emit_compact_size(self.key_data.len() + 1).expect("in-memory writers don't error");
self.type_value.consensus_encode(&mut buf).expect("in-memory writers don't error");

View File

@ -6,6 +6,7 @@
//! according to the BIP-174 specification.
use hashes::{hash160, ripemd160, sha256, sha256d};
use internals::compact_size;
use secp256k1::XOnlyPublicKey;
use super::map::{Input, Map, Output, PsbtSighashType};
@ -22,7 +23,7 @@ use crate::taproot::{
};
use crate::transaction::{Transaction, TxOut};
use crate::witness::Witness;
use crate::VarInt;
/// A trait for serializing a value as raw data for insertion into PSBT
/// key-value maps.
pub(crate) trait Serialize {
@ -355,8 +356,8 @@ impl Serialize for TapTree {
let capacity = self
.script_leaves()
.map(|l| {
l.script().len() + VarInt::from(l.script().len()).size() // script version
+ 1 // Merkle branch
l.script().len() + compact_size::encoded_size(l.script().len()) // script version
+ 1 // merkle branch
+ 1 // leaf version
})
.sum::<usize>();

View File

@ -7,7 +7,7 @@
use hashes::{sha256d, HashEngine};
use crate::consensus::{encode, Encodable};
use crate::consensus::encode::WriteExt;
#[rustfmt::skip]
#[doc(inline)]
@ -209,8 +209,7 @@ pub fn signed_msg_hash(msg: impl AsRef<[u8]>) -> sha256d::Hash {
let msg_bytes = msg.as_ref();
let mut engine = sha256d::Hash::engine();
engine.input(BITCOIN_SIGNED_MSG_PREFIX);
let msg_len = encode::VarInt::from(msg_bytes.len());
msg_len.consensus_encode(&mut engine).expect("engines don't error");
engine.emit_compact_size(msg_bytes.len()).expect("engines don't error");
engine.input(msg_bytes);
sha256d::Hash::from_engine(engine)
}

View File

@ -1153,8 +1153,8 @@ impl ControlBlock {
/// Serializes the control block.
///
/// This would be required when using [`ControlBlock`] as a witness element while spending an
/// output via script path. This serialization does not include the [`crate::VarInt`] prefix that would
/// be applied when encoding this element as a witness.
/// output via script path. This serialization does not include the varint prefix that would be
/// applied when encoding this element as a witness.
pub fn serialize(&self) -> Vec<u8> {
let mut buf = Vec::with_capacity(self.size());
self.encode(&mut buf).expect("writers don't error");

View File

@ -30,7 +30,20 @@ const MAX_ENCODING_SIZE: usize = 9;
/// - 9 otherwise.
#[inline]
pub fn encoded_size(value: impl ToU64) -> usize {
match value.to_u64() {
encoded_size_const(value.to_u64())
}
/// Returns the number of bytes used to encode this `CompactSize` value (in const context).
///
/// # Returns
///
/// - 1 for 0..=0xFC
/// - 3 for 0xFD..=(2^16-1)
/// - 5 for 0x10000..=(2^32-1)
/// - 9 otherwise.
#[inline]
pub const fn encoded_size_const(value: u64) -> usize {
match value {
0..=0xFC => 1,
0xFD..=0xFFFF => 3,
0x10000..=0xFFFFFFFF => 5,