diff --git a/src/blockdata/block.rs b/src/blockdata/block.rs index 0c53ed42..e5cd3f20 100644 --- a/src/blockdata/block.rs +++ b/src/blockdata/block.rs @@ -233,17 +233,27 @@ impl Block { bitcoin_merkle_root(hashes).into() } + /// The size of the header + the size of the varint with the tx count + the txs themselves + #[inline] + fn get_base_size(&self) -> usize { + 80 + VarInt(self.txdata.len() as u64).len() + } + /// Get the size of the block pub fn get_size(&self) -> usize { - // The size of the header + the size of the varint with the tx count + the txs themselves - let base_size = 80 + VarInt(self.txdata.len() as u64).len(); let txs_size: usize = self.txdata.iter().map(Transaction::get_size).sum(); - base_size + txs_size + self.get_base_size() + txs_size + } + + /// Get the strippedsize of the block + pub fn get_strippedsize(&self) -> usize { + let txs_size: usize = self.txdata.iter().map(Transaction::get_strippedsize).sum(); + self.get_base_size() + txs_size } /// Get the weight of the block pub fn get_weight(&self) -> usize { - let base_weight = WITNESS_SCALE_FACTOR * (80 + VarInt(self.txdata.len() as u64).len()); + let base_weight = WITNESS_SCALE_FACTOR * self.get_base_size(); let txs_weight: usize = self.txdata.iter().map(Transaction::get_weight).sum(); base_weight + txs_weight } @@ -372,6 +382,7 @@ mod tests { // [test] TODO: check the transaction data assert_eq!(real_decode.get_size(), some_block.len()); + assert_eq!(real_decode.get_strippedsize(), some_block.len()); assert_eq!(real_decode.get_weight(), some_block.len() * 4); // should be also ok for a non-witness block as commitment is optional in that case @@ -406,6 +417,7 @@ mod tests { // [test] TODO: check the transaction data assert_eq!(real_decode.get_size(), segwit_block.len()); + assert_eq!(real_decode.get_strippedsize(), 4283); assert_eq!(real_decode.get_weight(), 17168); assert!(real_decode.check_witness_commitment()); diff --git a/src/blockdata/transaction.rs b/src/blockdata/transaction.rs index 4ecac18a..4b143ef0 100644 --- a/src/blockdata/transaction.rs +++ b/src/blockdata/transaction.rs @@ -429,6 +429,39 @@ impl Transaction { self.get_scaled_size(1) } + /// Gets the "vsize" of this transaction. Will be `ceil(weight / 4.0)`. + #[inline] + pub fn get_vsize(&self) -> usize { + let weight = self.get_weight(); + (weight + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR + } + + /// Gets the size of this transaction excluding the witness data. + pub fn get_strippedsize(&self) -> usize { + let mut input_size = 0; + for input in &self.input { + input_size += 32 + 4 + 4 + // outpoint (32+4) + nSequence + VarInt(input.script_sig.len() as u64).len() + + input.script_sig.len(); + } + let mut output_size = 0; + for output in &self.output { + output_size += 8 + // value + VarInt(output.script_pubkey.len() as u64).len() + + output.script_pubkey.len(); + } + let non_input_size = + // version: + 4 + + // count varints: + VarInt(self.input.len() as u64).len() + + VarInt(self.output.len() as u64).len() + + output_size + + // lock_time + 4; + non_input_size + input_size + } + /// Internal utility function for get_{size,weight} fn get_scaled_size(&self, scale_factor: usize) -> usize { let mut input_weight = 0; @@ -857,6 +890,8 @@ mod tests { "a6eab3c14ab5272a58a5ba91505ba1a4b6d7a3a9fcbd187b6cd99a7b6d548cb7".to_string()); assert_eq!(realtx.get_weight(), tx_bytes.len()*WITNESS_SCALE_FACTOR); assert_eq!(realtx.get_size(), tx_bytes.len()); + assert_eq!(realtx.get_vsize(), tx_bytes.len()); + assert_eq!(realtx.get_strippedsize(), tx_bytes.len()); } #[test] @@ -887,8 +922,24 @@ mod tests { "f5864806e3565c34d1b41e716f72609d00b55ea5eac5b924c9719a842ef42206".to_string()); assert_eq!(format!("{:x}", realtx.wtxid()), "80b7d8a82d5d5bf92905b06f2014dd699e03837ca172e3a59d51426ebbe3e7f5".to_string()); - assert_eq!(realtx.get_weight(), 442); + const EXPECTED_WEIGHT: usize = 442; + assert_eq!(realtx.get_weight(), EXPECTED_WEIGHT); assert_eq!(realtx.get_size(), tx_bytes.len()); + assert_eq!(realtx.get_vsize(), 111); + // Since + // size = stripped_size + witness_size + // weight = WITNESS_SCALE_FACTOR * stripped_size + witness_size + // then, + // stripped_size = (weight - size) / (WITNESS_SCALE_FACTOR - 1) + let expected_strippedsize = (EXPECTED_WEIGHT - tx_bytes.len()) / (WITNESS_SCALE_FACTOR - 1); + assert_eq!(realtx.get_strippedsize(), expected_strippedsize); + // Construct a transaction without the witness data. + let mut tx_without_witness = realtx.clone(); + tx_without_witness.input.iter_mut().for_each(|input| input.witness.clear()); + assert_eq!(tx_without_witness.get_weight(), expected_strippedsize*WITNESS_SCALE_FACTOR); + assert_eq!(tx_without_witness.get_size(), expected_strippedsize); + assert_eq!(tx_without_witness.get_vsize(), expected_strippedsize); + assert_eq!(tx_without_witness.get_strippedsize(), expected_strippedsize); } #[test]