Skip to content

Commit 50bd321

Browse files
committed
transaction and block: use Weight as return type for weight methods
The API change is actually only a couple LOC. The real change is that we start using u64 arithmetic to compute weights rather than usize arithmetic, because Weight::from_wu wants a u64. To facilitate this we introduce the private Len64 trait which provides lengths in u64, and use it throughout the crate. This replaces 23 casts to usize or u64 with four.
1 parent b24571b commit 50bd321

File tree

6 files changed

+144
-92
lines changed

6 files changed

+144
-92
lines changed

src/block.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ use crate::dynafed;
2424
use crate::hashes::{Hash, sha256};
2525
use crate::Transaction;
2626
use crate::encode::{self, serialize, Decodable, Encodable, VarInt};
27-
use crate::{BlockHash, Script, TxMerkleNode};
27+
use crate::{BlockHash, Script, TxMerkleNode, Weight};
28+
use crate::Len64 as _;
2829

2930
/// Data related to block signatures
3031
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
@@ -365,17 +366,19 @@ impl Block {
365366
}
366367

367368
/// Get the size of the block
369+
#[deprecated(since = "0.26.0", note = "use Self::weight or Self::encoded_length instead")]
368370
pub fn size(&self) -> usize {
369371
// The size of the header + the size of the varint with the tx count + the txs themselves
370-
let base_size = serialize(&self.header).len() + VarInt(self.txdata.len() as u64).size();
371-
let txs_size: usize = self.txdata.iter().map(Transaction::size).sum();
372+
let base_size = serialize(&self.header).len() + VarInt(self.txdata.len64()).size();
373+
let txs_size = self.txdata.iter().map(Transaction::encoded_length).sum::<usize>();
372374
base_size + txs_size
373375
}
374376

375377
/// Get the weight of the block
376-
pub fn weight(&self) -> usize {
377-
let base_weight = 4 * (serialize(&self.header).len() + VarInt(self.txdata.len() as u64).size());
378-
let txs_weight: usize = self.txdata.iter().map(Transaction::weight).sum();
378+
pub fn weight(&self) -> Weight {
379+
let base_weight = Weight::from_vb(self.header.encoded_len64() + VarInt(self.txdata.len64()).len64())
380+
.expect("base weight does not overflow the Weight type");
381+
let txs_weight = self.txdata.iter().map(Transaction::weight).sum::<Weight>();
379382
base_weight + txs_weight
380383
}
381384
}
@@ -454,8 +457,8 @@ mod tests {
454457
assert_eq!(block.header.version, 0x2000_0000);
455458
assert_eq!(block.header.height, 2);
456459
assert_eq!(block.txdata.len(), 1);
457-
assert_eq!(block.size(), serialize(&block).len());
458-
assert_eq!(block.weight(), 1089);
460+
assert_eq!(block.encoded_length(), serialize(&block).len());
461+
assert_eq!(block.weight().to_wu(), 1089);
459462

460463
// Block with 3 transactions ... the rangeproofs are very large :)
461464
let block: Block = hex_deserialize!(
@@ -666,7 +669,7 @@ mod tests {
666669
assert_eq!(block.header.version, 0x2000_0000);
667670
assert_eq!(block.header.height, 1);
668671
assert_eq!(block.txdata.len(), 3);
669-
assert_eq!(block.size(), serialize(&block).len());
672+
assert_eq!(block.encoded_length(), serialize(&block).len());
670673

671674
// 2-of-3 signed block from Liquid integration tests
672675
let block: Block = hex_deserialize!(

src/encode.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use secp256k1_zkp::{self, RangeProof, SurjectionProof, Tweak};
2424
use crate::hashes::{sha256, Hash};
2525
use crate::pset;
2626
use crate::transaction::{Transaction, TxIn, TxOut};
27+
use crate::Len64 as _;
2728

2829
pub use bitcoin::{self, consensus::encode::MAX_VEC_SIZE};
2930

@@ -224,7 +225,7 @@ pub(crate) fn consensus_encode_with_size<S: crate::WriteExt>(
224225
data: &[u8],
225226
mut s: S,
226227
) -> Result<usize, Error> {
227-
let vi_len = VarInt(data.len() as u64).consensus_encode(&mut s)?;
228+
let vi_len = VarInt(data.len64()).consensus_encode(&mut s)?;
228229
s.emit_slice(data)?;
229230
Ok(vi_len + data.len())
230231
}
@@ -336,7 +337,7 @@ macro_rules! impl_vec {
336337
#[inline]
337338
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, Error> {
338339
let mut len = 0;
339-
len += VarInt(self.len() as u64).consensus_encode(&mut s)?;
340+
len += VarInt(self.len64()).consensus_encode(&mut s)?;
340341
for c in self.iter() {
341342
len += c.consensus_encode(&mut s)?;
342343
}

src/lib.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,39 @@ pub use crate::transaction::{
9898
AssetIssuance, EcdsaSighashType, OutPoint, PeginData, PegoutData, Transaction, TxIn,
9999
TxInWitness, TxOut, TxOutWitness,
100100
};
101+
102+
/// Utility trait for producing lengths in u64, for use in weight computations.
103+
trait Len64 {
104+
fn len64(&self) -> u64;
105+
}
106+
107+
impl<T> Len64 for [T] {
108+
fn len64(&self) -> u64 { self.len() as u64 }
109+
}
110+
impl<T> Len64 for &[T] {
111+
fn len64(&self) -> u64 { (*self).len64() }
112+
}
113+
impl<T> Len64 for Vec<T> {
114+
fn len64(&self) -> u64 { self[..].len64() }
115+
}
116+
impl Len64 for crate::script::Script {
117+
fn len64(&self) -> u64 { self[..].len64() }
118+
}
119+
impl Len64 for secp256k1_zkp::RangeProof {
120+
fn len64(&self) -> u64 { self.serialize().len64() }
121+
}
122+
impl Len64 for secp256k1_zkp::SurjectionProof {
123+
fn len64(&self) -> u64 { self.serialize().len64() }
124+
}
125+
impl Len64 for bitcoin::VarInt {
126+
fn len64(&self) -> u64 { self.size() as u64 }
127+
}
128+
impl Len64 for crate::encode::VarInt {
129+
fn len64(&self) -> u64 { self.size() as u64 }
130+
}
131+
impl<T: Len64> Len64 for Option<T> {
132+
fn len64(&self) -> u64 { self.as_ref().map_or(0, T::len64) }
133+
}
134+
impl<T: Len64> Len64 for Box<T> {
135+
fn len64(&self) -> u64 { (**self).len64() }
136+
}

src/pset/raw.rs

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -99,24 +99,31 @@ impl fmt::Display for Key {
9999

100100
impl Decodable for Key {
101101
fn consensus_decode<D: io::Read>(mut d: D) -> Result<Self, encode::Error> {
102+
use core::convert::TryFrom;
103+
102104
let VarInt(byte_size): VarInt = Decodable::consensus_decode(&mut d)?;
103105

104106
if byte_size == 0 {
105107
return Err(Error::NoMorePairs.into());
106108
}
109+
let key_byte_size = match usize::try_from(byte_size) {
110+
Ok(n) => n - 1,
111+
Err(_) => return Err(encode::Error::OversizedVectorAllocation {
112+
requested: usize::MAX,
113+
max: MAX_VEC_SIZE,
114+
}),
115+
};
107116

108-
let key_byte_size: u64 = byte_size - 1;
109-
110-
if key_byte_size > MAX_VEC_SIZE as u64 {
117+
if key_byte_size > MAX_VEC_SIZE {
111118
return Err(encode::Error::OversizedVectorAllocation {
112-
requested: key_byte_size as usize,
119+
requested: key_byte_size,
113120
max: MAX_VEC_SIZE,
114121
});
115122
}
116123

117124
let type_value: u8 = Decodable::consensus_decode(&mut d)?;
118125

119-
let mut key = Vec::with_capacity(key_byte_size as usize);
126+
let mut key = Vec::with_capacity(key_byte_size);
120127
for _ in 0..key_byte_size {
121128
key.push(Decodable::consensus_decode(&mut d)?);
122129
}
@@ -127,8 +134,10 @@ impl Decodable for Key {
127134

128135
impl Encodable for Key {
129136
fn consensus_encode<S: io::Write>(&self, mut s: S) -> Result<usize, encode::Error> {
137+
use crate::Len64 as _;
138+
130139
let mut len = 0;
131-
len += VarInt((self.key.len() + 1) as u64).consensus_encode(&mut s)?;
140+
len += VarInt(self.key.len64() + 1).consensus_encode(&mut s)?;
132141

133142
len += self.type_value.consensus_encode(&mut s)?;
134143

src/script.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ use secp256k1_zkp::{Verification, Secp256k1};
3333
use crate::encode::{self, Decodable, Encodable};
3434
use crate::hashes::Hash;
3535
use crate::{hex, opcodes, ScriptHash, WScriptHash, PubkeyHash, WPubkeyHash};
36+
use crate::Len64 as _;
3637

3738
use bitcoin::PublicKey;
3839

@@ -780,7 +781,7 @@ impl Builder {
780781
#[must_use]
781782
pub fn push_slice(mut self, data: &[u8]) -> Builder {
782783
// Start with a PUSH opcode
783-
match data.len() as u64 {
784+
match data.len64() {
784785
n if n < opcodes::Ordinary::OP_PUSHDATA1 as u64 => { self.0.push(n as u8); },
785786
n if n < 0x100 => {
786787
self.0.push(opcodes::Ordinary::OP_PUSHDATA1.into_u8());

0 commit comments

Comments
 (0)