From 0419fa278bfe13c96f216321fd18969e48c842b2 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Thu, 24 Aug 2023 10:37:53 +1000 Subject: [PATCH] Add VarInt from implementations by way of macro Throughout the codebase we cast values to `u64` when constructing a `VarInt`. We can make the code marginally cleaner by adding `From` impls for all unsigned integer types less than or equal to 64 bits. Also allows us to (possibly unnecessarily) comment the cast in a single place. --- bitcoin/src/bip152.rs | 2 +- bitcoin/src/bip158.rs | 2 +- bitcoin/src/blockdata/block.rs | 2 +- bitcoin/src/blockdata/transaction.rs | 6 ++---- bitcoin/src/blockdata/witness.rs | 12 ++++++------ bitcoin/src/consensus/encode.rs | 23 +++++++++++++++++++---- bitcoin/src/merkle_tree/block.rs | 2 +- bitcoin/src/p2p/address.rs | 2 +- bitcoin/src/p2p/message.rs | 2 +- bitcoin/src/psbt/raw.rs | 2 +- bitcoin/src/psbt/serialize.rs | 2 +- bitcoin/src/sign_message.rs | 2 +- 12 files changed, 36 insertions(+), 23 deletions(-) diff --git a/bitcoin/src/bip152.rs b/bitcoin/src/bip152.rs index 17c296c0..39679a6c 100644 --- a/bitcoin/src/bip152.rs +++ b/bitcoin/src/bip152.rs @@ -74,7 +74,7 @@ impl convert::AsRef for PrefilledTransaction { impl Encodable for PrefilledTransaction { #[inline] fn consensus_encode(&self, mut s: &mut S) -> Result { - Ok(VarInt(self.idx as u64).consensus_encode(&mut s)? + self.tx.consensus_encode(&mut s)?) + Ok(VarInt::from(self.idx).consensus_encode(&mut s)? + self.tx.consensus_encode(&mut s)?) } } diff --git a/bitcoin/src/bip158.rs b/bitcoin/src/bip158.rs index 2f794ca8..603fae65 100644 --- a/bitcoin/src/bip158.rs +++ b/bitcoin/src/bip158.rs @@ -391,7 +391,7 @@ impl<'a, W: io::Write> GcsFilterWriter<'a, W> { mapped.sort_unstable(); // write number of elements as varint - let mut wrote = VarInt(mapped.len() as u64).consensus_encode(&mut self.writer)?; + let mut wrote = VarInt::from(mapped.len()).consensus_encode(&mut self.writer)?; // write out deltas of sorted values into a Golonb-Rice coded bit stream let mut writer = BitStreamWriter::new(self.writer); diff --git a/bitcoin/src/blockdata/block.rs b/bitcoin/src/blockdata/block.rs index 967e8682..e3fa9334 100644 --- a/bitcoin/src/blockdata/block.rs +++ b/bitcoin/src/blockdata/block.rs @@ -276,7 +276,7 @@ impl Block { } /// base_size == size of header + size of encoded transaction count. - fn base_size(&self) -> usize { 80 + VarInt(self.txdata.len() as u64).len() } + fn base_size(&self) -> usize { 80 + VarInt::from(self.txdata.len()).len() } /// Returns the size of the block. /// diff --git a/bitcoin/src/blockdata/transaction.rs b/bitcoin/src/blockdata/transaction.rs index 03d7175b..c4a5e67f 100644 --- a/bitcoin/src/blockdata/transaction.rs +++ b/bitcoin/src/blockdata/transaction.rs @@ -227,7 +227,7 @@ impl TxIn { // Size in vbytes: // previous_output (36) + script_sig varint len + script_sig push + sequence (4) Weight::from_non_witness_data_size( - (36 + VarInt(script_sig_size as u64).len() + script_sig_size + 4) as u64, + (36 + VarInt::from(script_sig_size).len() + script_sig_size + 4) as u64, ) } @@ -501,9 +501,7 @@ impl TxOut { let script_len = self.script_pubkey.len(); // In vbytes: // value (8) + script varint len + script push - Weight::from_non_witness_data_size( - (8 + VarInt(script_len as u64).len() + script_len) as u64, - ) + Weight::from_non_witness_data_size((8 + VarInt::from(script_len).len() + script_len) as u64) } /// Creates a `TxOut` with given script and the smallest possible `value` that is **not** dust diff --git a/bitcoin/src/blockdata/witness.rs b/bitcoin/src/blockdata/witness.rs index 390ff0b0..53a846ee 100644 --- a/bitcoin/src/blockdata/witness.rs +++ b/bitcoin/src/blockdata/witness.rs @@ -213,7 +213,7 @@ fn resize_if_needed(vec: &mut Vec, required_len: usize) { impl Encodable for Witness { fn consensus_encode(&self, w: &mut W) -> Result { - let len = VarInt(self.witness_elements as u64); + 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; @@ -233,14 +233,14 @@ impl Witness { let index_size = witness_elements * 4; let content_size = slice .iter() - .map(|elem| elem.as_ref().len() + VarInt(elem.as_ref().len() as u64).len()) + .map(|elem| elem.as_ref().len() + VarInt::from(elem.as_ref().len()).len()) .sum(); let mut content = vec![0u8; content_size + index_size]; let mut cursor = 0usize; for (i, elem) in slice.iter().enumerate() { encode_cursor(&mut content, content_size, i, cursor); - let elem_len_varint = VarInt(elem.as_ref().len() as u64); + let elem_len_varint = VarInt::from(elem.as_ref().len()); elem_len_varint .consensus_encode(&mut &mut content[cursor..cursor + elem_len_varint.len()]) .expect("writers on vec don't errors, space granted by content_size"); @@ -268,8 +268,8 @@ impl Witness { /// Returns the bytes required when this Witness is consensus encoded. pub fn serialized_len(&self) -> usize { - self.iter().map(|el| VarInt(el.len() as u64).len() + el.len()).sum::() - + VarInt(self.witness_elements as u64).len() + self.iter().map(|el| VarInt::from(el.len()).len() + el.len()).sum::() + + VarInt::from(self.witness_elements).len() } /// Clear the witness. @@ -288,7 +288,7 @@ impl Witness { fn push_slice(&mut self, new_element: &[u8]) { self.witness_elements += 1; let previous_content_end = self.indices_start; - let element_len_varint = VarInt(new_element.len() as u64); + let element_len_varint = VarInt::from(new_element.len()); let current_content_len = self.content.len(); let new_item_total_len = element_len_varint.len() + new_element.len(); self.content.resize(current_content_len + new_item_total_len + 4, 0); diff --git a/bitcoin/src/consensus/encode.rs b/bitcoin/src/consensus/encode.rs index 297d1c08..1aa927f0 100644 --- a/bitcoin/src/consensus/encode.rs +++ b/bitcoin/src/consensus/encode.rs @@ -399,6 +399,21 @@ impl VarInt { } } +/// Implements `From 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 as u64) } + } + )* + } +} +impl_var_int_from!(u8, u16, u32, u64, usize); + impl Encodable for VarInt { #[inline] fn consensus_encode(&self, w: &mut W) -> Result { @@ -436,7 +451,7 @@ impl Decodable for VarInt { if x < 0x100000000 { Err(self::Error::NonMinimalVarInt) } else { - Ok(VarInt(x)) + Ok(VarInt::from(x)) } } 0xFE => { @@ -444,7 +459,7 @@ impl Decodable for VarInt { if x < 0x10000 { Err(self::Error::NonMinimalVarInt) } else { - Ok(VarInt(x as u64)) + Ok(VarInt::from(x)) } } 0xFD => { @@ -452,10 +467,10 @@ impl Decodable for VarInt { if x < 0xFD { Err(self::Error::NonMinimalVarInt) } else { - Ok(VarInt(x as u64)) + Ok(VarInt::from(x)) } } - n => Ok(VarInt(n as u64)), + n => Ok(VarInt::from(n)), } } } diff --git a/bitcoin/src/merkle_tree/block.rs b/bitcoin/src/merkle_tree/block.rs index 6fff7650..076e55dc 100644 --- a/bitcoin/src/merkle_tree/block.rs +++ b/bitcoin/src/merkle_tree/block.rs @@ -436,7 +436,7 @@ impl Encodable for PartialMerkleTree { ret += self.hashes.consensus_encode(w)?; let nb_bytes_for_bits = (self.bits.len() + 7) / 8; - ret += encode::VarInt(nb_bytes_for_bits as u64).consensus_encode(w)?; + ret += encode::VarInt::from(nb_bytes_for_bits).consensus_encode(w)?; for chunk in self.bits.chunks(8) { let mut byte = 0u8; for (i, bit) in chunk.iter().enumerate() { diff --git a/bitcoin/src/p2p/address.rs b/bitcoin/src/p2p/address.rs index 58678bc9..11cbd882 100644 --- a/bitcoin/src/p2p/address.rs +++ b/bitcoin/src/p2p/address.rs @@ -148,7 +148,7 @@ impl Encodable for AddrV2 { bytes: &[u8], ) -> Result { let len = network.consensus_encode(w)? - + VarInt(bytes.len() as u64).consensus_encode(w)? + + VarInt::from(bytes.len()).consensus_encode(w)? + bytes.len(); w.emit_slice(bytes)?; Ok(len) diff --git a/bitcoin/src/p2p/message.rs b/bitcoin/src/p2p/message.rs index 8b048af2..8b76e80e 100644 --- a/bitcoin/src/p2p/message.rs +++ b/bitcoin/src/p2p/message.rs @@ -335,7 +335,7 @@ impl<'a> Encodable for HeaderSerializationWrapper<'a> { #[inline] fn consensus_encode(&self, w: &mut W) -> Result { let mut len = 0; - len += VarInt(self.0.len() as u64).consensus_encode(w)?; + len += VarInt::from(self.0.len()).consensus_encode(w)?; for header in self.0.iter() { len += header.consensus_encode(w)?; len += 0u8.consensus_encode(w)?; diff --git a/bitcoin/src/psbt/raw.rs b/bitcoin/src/psbt/raw.rs index aa312413..3e80366b 100644 --- a/bitcoin/src/psbt/raw.rs +++ b/bitcoin/src/psbt/raw.rs @@ -104,7 +104,7 @@ impl Key { impl Serialize for Key { fn serialize(&self) -> Vec { let mut buf = Vec::new(); - VarInt((self.key.len() + 1) as u64) + VarInt::from(self.key.len() + 1) .consensus_encode(&mut buf) .expect("in-memory writers don't error"); diff --git a/bitcoin/src/psbt/serialize.rs b/bitcoin/src/psbt/serialize.rs index 3f9c57ad..43245b90 100644 --- a/bitcoin/src/psbt/serialize.rs +++ b/bitcoin/src/psbt/serialize.rs @@ -342,7 +342,7 @@ impl Serialize for TapTree { let capacity = self .script_leaves() .map(|l| { - l.script().len() + VarInt(l.script().len() as u64).len() // script version + l.script().len() + VarInt::from(l.script().len()).len() // script version + 1 // merkle branch + 1 // leaf version }) diff --git a/bitcoin/src/sign_message.rs b/bitcoin/src/sign_message.rs index abaf4651..bb9d47b7 100644 --- a/bitcoin/src/sign_message.rs +++ b/bitcoin/src/sign_message.rs @@ -198,7 +198,7 @@ mod message_signing { pub fn signed_msg_hash(msg: &str) -> sha256d::Hash { let mut engine = sha256d::Hash::engine(); engine.input(BITCOIN_SIGNED_MSG_PREFIX); - let msg_len = encode::VarInt(msg.len() as u64); + let msg_len = encode::VarInt::from(msg.len()); msg_len.consensus_encode(&mut engine).expect("engines don't error"); engine.input(msg.as_bytes()); sha256d::Hash::from_engine(engine)