Skip to content

Commit 2fd0125

Browse files
committed
Introduce Witness struct mainly to improve ser/de performance while keeping most usability.
Witness struct is in place of the Vec<Vec<u8>> we have before this commit. from_vec() and to_vec() methods are provided to switch between this type and Vec<Vec<u8>> Moreover, implementation of Default, Iterator and others allows to have similar behaviour but using a single Vec prevent many allocations during deserialization which in turns results in better performance, even 20% better perfomance on recent block. last() and second_to_last() allows to access respective element without going through costly Vec transformation
1 parent fe43e3c commit 2fd0125

File tree

8 files changed

+436
-36
lines changed

8 files changed

+436
-36
lines changed

src/blockdata/block.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,10 @@ impl Block {
199199
o.script_pubkey[0..6] == [0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed] }) {
200200
let commitment = WitnessCommitment::from_slice(&coinbase.output[pos].script_pubkey.as_bytes()[6..38]).unwrap();
201201
// witness reserved value is in coinbase input witness
202-
if coinbase.input[0].witness.len() == 1 && coinbase.input[0].witness[0].len() == 32 {
202+
let witness_vec: Vec<_> = coinbase.input[0].witness.iter().collect();
203+
if witness_vec.len() == 1 && witness_vec[0].len() == 32 {
203204
match self.witness_root() {
204-
Some(witness_root) => return commitment == Self::compute_witness_commitment(&witness_root, coinbase.input[0].witness[0].as_slice()),
205+
Some(witness_root) => return commitment == Self::compute_witness_commitment(&witness_root, witness_vec[0]),
205206
None => return false,
206207
}
207208
}

src/blockdata/constants.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use blockdata::opcodes;
2929
use blockdata::script;
3030
use blockdata::transaction::{OutPoint, Transaction, TxOut, TxIn};
3131
use blockdata::block::{Block, BlockHeader};
32+
use blockdata::witness::Witness;
3233
use network::constants::Network;
3334
use util::uint::Uint256;
3435

@@ -93,7 +94,7 @@ fn bitcoin_genesis_tx() -> Transaction {
9394
previous_output: OutPoint::null(),
9495
script_sig: in_script,
9596
sequence: MAX_SEQUENCE,
96-
witness: vec![],
97+
witness: Witness::default(),
9798
});
9899

99100
// Outputs

src/blockdata/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,5 @@ pub mod opcodes;
2323
pub mod script;
2424
pub mod transaction;
2525
pub mod block;
26+
pub mod witness;
2627

src/blockdata/transaction.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ use util::endian;
3636
use blockdata::constants::WITNESS_SCALE_FACTOR;
3737
#[cfg(feature="bitcoinconsensus")] use blockdata::script;
3838
use blockdata::script::Script;
39+
use blockdata::witness::Witness;
3940
use consensus::{encode, Decodable, Encodable};
4041
use consensus::encode::MAX_VEC_SIZE;
4142
use hash_types::{SigHash, Txid, Wtxid};
@@ -197,7 +198,7 @@ pub struct TxIn {
197198
/// Encodable/Decodable, as it is (de)serialized at the end of the full
198199
/// Transaction. It *is* (de)serialized with the rest of the TxIn in other
199200
/// (de)serialization routines.
200-
pub witness: Vec<Vec<u8>>
201+
pub witness: Witness
201202
}
202203

203204
impl Default for TxIn {
@@ -206,7 +207,7 @@ impl Default for TxIn {
206207
previous_output: OutPoint::default(),
207208
script_sig: Script::new(),
208209
sequence: u32::max_value(),
209-
witness: Vec::new(),
210+
witness: Witness::default(),
210211
}
211212
}
212213
}
@@ -280,7 +281,7 @@ impl Transaction {
280281
let cloned_tx = Transaction {
281282
version: self.version,
282283
lock_time: self.lock_time,
283-
input: self.input.iter().map(|txin| TxIn { script_sig: Script::new(), witness: vec![], .. *txin }).collect(),
284+
input: self.input.iter().map(|txin| TxIn { script_sig: Script::new(), witness: Witness::default(), .. *txin }).collect(),
284285
output: self.output.clone(),
285286
};
286287
cloned_tx.txid().into()
@@ -357,7 +358,7 @@ impl Transaction {
357358
previous_output: self.input[input_index].previous_output,
358359
script_sig: script_pubkey.clone(),
359360
sequence: self.input[input_index].sequence,
360-
witness: vec![],
361+
witness: Witness::default(),
361362
}];
362363
} else {
363364
tx.input = Vec::with_capacity(self.input.len());
@@ -366,7 +367,7 @@ impl Transaction {
366367
previous_output: input.previous_output,
367368
script_sig: if n == input_index { script_pubkey.clone() } else { Script::new() },
368369
sequence: if n != input_index && (sighash == EcdsaSigHashType::Single || sighash == EcdsaSigHashType::None) { 0 } else { input.sequence },
369-
witness: vec![],
370+
witness: Witness::default(),
370371
});
371372
}
372373
}
@@ -473,10 +474,7 @@ impl Transaction {
473474
input.script_sig.len());
474475
if !input.witness.is_empty() {
475476
inputs_with_witnesses += 1;
476-
input_weight += VarInt(input.witness.len() as u64).len();
477-
for elem in &input.witness {
478-
input_weight += VarInt(elem.len() as u64).len() + elem.len();
479-
}
477+
input_weight += input.witness.serialized_len();
480478
}
481479
}
482480
let mut output_size = 0;
@@ -578,7 +576,7 @@ impl Decodable for TxIn {
578576
previous_output: Decodable::consensus_decode(&mut d)?,
579577
script_sig: Decodable::consensus_decode(&mut d)?,
580578
sequence: Decodable::consensus_decode(d)?,
581-
witness: vec![],
579+
witness: Witness::default(),
582580
})
583581
}
584582
}
@@ -1433,6 +1431,7 @@ mod tests {
14331431
use hashes::hex::FromHex;
14341432
use std::collections::HashMap;
14351433
use blockdata::script;
1434+
use blockdata::witness::Witness;
14361435

14371436
// a random recent segwit transaction from blockchain using both old and segwit inputs
14381437
let mut spending: Transaction = deserialize(Vec::from_hex("020000000001031cfbc8f54fbfa4a33a30068841371f80dbfe166211242213188428f437445c91000000006a47304402206fbcec8d2d2e740d824d3d36cc345b37d9f65d665a99f5bd5c9e8d42270a03a8022013959632492332200c2908459547bf8dbf97c65ab1a28dec377d6f1d41d3d63e012103d7279dfb90ce17fe139ba60a7c41ddf605b25e1c07a4ddcb9dfef4e7d6710f48feffffff476222484f5e35b3f0e43f65fc76e21d8be7818dd6a989c160b1e5039b7835fc00000000171600140914414d3c94af70ac7e25407b0689e0baa10c77feffffffa83d954a62568bbc99cc644c62eb7383d7c2a2563041a0aeb891a6a4055895570000000017160014795d04cc2d4f31480d9a3710993fbd80d04301dffeffffff06fef72f000000000017a91476fd7035cd26f1a32a5ab979e056713aac25796887a5000f00000000001976a914b8332d502a529571c6af4be66399cd33379071c588ac3fda0500000000001976a914fc1d692f8de10ae33295f090bea5fe49527d975c88ac522e1b00000000001976a914808406b54d1044c429ac54c0e189b0d8061667e088ac6eb68501000000001976a914dfab6085f3a8fb3e6710206a5a959313c5618f4d88acbba20000000000001976a914eb3026552d7e3f3073457d0bee5d4757de48160d88ac0002483045022100bee24b63212939d33d513e767bc79300051f7a0d433c3fcf1e0e3bf03b9eb1d70220588dc45a9ce3a939103b4459ce47500b64e23ab118dfc03c9caa7d6bfc32b9c601210354fd80328da0f9ae6eef2b3a81f74f9a6f66761fadf96f1d1d22b1fd6845876402483045022100e29c7e3a5efc10da6269e5fc20b6a1cb8beb92130cc52c67e46ef40aaa5cac5f0220644dd1b049727d991aece98a105563416e10a5ac4221abac7d16931842d5c322012103960b87412d6e169f30e12106bdf70122aabb9eb61f455518322a18b920a4dfa887d30700")
@@ -1471,7 +1470,9 @@ mod tests {
14711470
}).is_err());
14721471

14731472
// test that we get a failure if we corrupt a signature
1474-
spending.input[1].witness[0][10] = 42;
1473+
let mut witness: Vec<_> = spending.input[1].witness.to_vec();
1474+
witness[0][10] = 42;
1475+
spending.input[1].witness = Witness::from_vec(witness);
14751476
match spending.verify(|point: &OutPoint| {
14761477
if let Some(tx) = spent3.remove(&point.txid) {
14771478
return tx.output.get(point.vout as usize).cloned();

0 commit comments

Comments
 (0)