2022-06-29 04:05:31 +00:00
|
|
|
// SPDX-License-Identifier: CC0-1.0
|
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
//! Witness
|
|
|
|
//!
|
|
|
|
//! This module contains the [`Witness`] struct and related methods to operate on it
|
|
|
|
//!
|
|
|
|
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
use core::fmt;
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
use core::ops::Index;
|
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::consensus::encode::{Error, MAX_VEC_SIZE};
|
|
|
|
use crate::consensus::{Decodable, Encodable, WriteExt};
|
2023-09-21 03:35:12 +00:00
|
|
|
use crate::crypto::ecdsa;
|
2022-05-02 22:13:57 +00:00
|
|
|
use crate::io::{self, Read, Write};
|
|
|
|
use crate::prelude::*;
|
2022-11-07 23:32:52 +00:00
|
|
|
use crate::taproot::TAPROOT_ANNEX_PREFIX;
|
2023-02-21 23:01:26 +00:00
|
|
|
use crate::{Script, VarInt};
|
2021-10-05 13:07:55 +00:00
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// The Witness is the data used to unlock bitcoin since the [segwit upgrade].
|
2021-10-05 13:07:55 +00:00
|
|
|
///
|
2022-11-14 17:50:17 +00:00
|
|
|
/// Can be logically seen as an array of bytestrings, i.e. `Vec<Vec<u8>>`, and it is serialized on the wire
|
|
|
|
/// in that format. You can convert between this type and `Vec<Vec<u8>>` by using [`Witness::from_slice`]
|
|
|
|
/// and [`Witness::to_vec`].
|
2021-10-05 13:07:55 +00:00
|
|
|
///
|
|
|
|
/// For serialization and deserialization performance it is stored internally as a single `Vec`,
|
|
|
|
/// saving some allocations.
|
|
|
|
///
|
2022-10-31 01:23:10 +00:00
|
|
|
/// [segwit upgrade]: <https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki>
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
2021-10-05 13:07:55 +00:00
|
|
|
pub struct Witness {
|
2022-11-20 22:27:16 +00:00
|
|
|
/// Contains the witness `Vec<Vec<u8>>` serialization without the initial varint indicating the
|
2022-10-31 01:23:10 +00:00
|
|
|
/// number of elements (which is stored in `witness_elements`).
|
2021-10-05 13:07:55 +00:00
|
|
|
content: Vec<u8>,
|
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// The number of elements in the witness.
|
|
|
|
///
|
|
|
|
/// Stored separately (instead of as a VarInt in the initial part of content) so that methods
|
|
|
|
/// like [`Witness::push`] don't have to shift the entire array.
|
2021-10-05 13:07:55 +00:00
|
|
|
witness_elements: usize,
|
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// This is the valid index pointing to the beginning of the index area. This area is 4 *
|
|
|
|
/// stack_size bytes at the end of the content vector which stores the indices of each item.
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
indices_start: usize,
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
impl fmt::Debug for Witness {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
|
|
|
if f.alternate() {
|
|
|
|
fmt_debug_pretty(self, f)
|
|
|
|
} else {
|
|
|
|
fmt_debug(self, f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fmt_debug(w: &Witness, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
|
|
|
#[rustfmt::skip]
|
|
|
|
let comma_or_close = |current_index, last_index| {
|
|
|
|
if current_index == last_index { "]" } else { ", " }
|
|
|
|
};
|
|
|
|
|
|
|
|
f.write_str("Witness: { ")?;
|
2023-07-14 20:37:11 +00:00
|
|
|
write!(f, "indices: {}, ", w.witness_elements)?;
|
|
|
|
write!(f, "indices_start: {}, ", w.indices_start)?;
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
f.write_str("witnesses: [")?;
|
|
|
|
|
|
|
|
let instructions = w.iter();
|
2023-08-28 23:01:25 +00:00
|
|
|
match instructions.len().checked_sub(1) {
|
|
|
|
Some(last_instruction) => {
|
|
|
|
for (i, instruction) in instructions.enumerate() {
|
|
|
|
let bytes = instruction.iter();
|
|
|
|
match bytes.len().checked_sub(1) {
|
|
|
|
Some(last_byte) => {
|
|
|
|
f.write_str("[")?;
|
|
|
|
for (j, byte) in bytes.enumerate() {
|
|
|
|
write!(f, "{:#04x}", byte)?;
|
|
|
|
f.write_str(comma_or_close(j, last_byte))?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
// This is possible because the varint is not part of the instruction (see Iter).
|
|
|
|
write!(f, "[]")?;
|
|
|
|
}
|
2023-08-16 23:34:46 +00:00
|
|
|
}
|
2023-08-28 23:01:25 +00:00
|
|
|
f.write_str(comma_or_close(i, last_instruction))?;
|
2023-08-16 23:34:46 +00:00
|
|
|
}
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
}
|
2023-08-28 23:01:25 +00:00
|
|
|
None => {
|
|
|
|
// Witnesses can be empty because the 0x00 var int is not stored in content.
|
|
|
|
write!(f, "]")?;
|
|
|
|
}
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 20:37:11 +00:00
|
|
|
f.write_str(" }")
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn fmt_debug_pretty(w: &Witness, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
|
2023-07-14 20:37:11 +00:00
|
|
|
f.write_str("Witness: {\n")?;
|
|
|
|
writeln!(f, " indices: {},", w.witness_elements)?;
|
|
|
|
writeln!(f, " indices_start: {},", w.indices_start)?;
|
|
|
|
f.write_str(" witnesses: [\n")?;
|
|
|
|
|
|
|
|
for instruction in w.iter() {
|
|
|
|
f.write_str(" [")?;
|
|
|
|
for (j, byte) in instruction.iter().enumerate() {
|
|
|
|
if j > 0 {
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
f.write_str(", ")?;
|
|
|
|
}
|
2023-07-14 20:37:11 +00:00
|
|
|
write!(f, "{:#04x}", byte)?;
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
}
|
2023-07-14 20:37:11 +00:00
|
|
|
f.write_str("],\n")?;
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 20:37:11 +00:00
|
|
|
writeln!(f, " ],")?;
|
|
|
|
writeln!(f, "}}")
|
Manually implement Debug on Witness
The current derived debug implementation on `Witness` prints the content
field as an array of integers. We can do better than this by manually
implementing `Debug`.
With this applied `Witness` is printed as follows: (first line is `{:?}`
and the next is `{:#?}`):
Using `{:?}`:
```
Witness: { indices: 3, indices_start: 8, witnesses: [[0x00], [0x02, 0x03], [0x04, 0x05]] }
```
Using `{:#?}`:
```
Witness: {
indices: 3,
indices_start: 8,
witnesses: [
[0x00],
[0x02, 0x03],
[0x04, 0x05],
],
}
```
2023-06-20 00:22:48 +00:00
|
|
|
}
|
|
|
|
|
2022-11-05 19:45:00 +00:00
|
|
|
/// An iterator returning individual witness elements.
|
2022-06-13 13:39:34 +00:00
|
|
|
pub struct Iter<'a> {
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
inner: &'a [u8],
|
|
|
|
indices_start: usize,
|
|
|
|
current_index: usize,
|
2022-06-13 13:39:34 +00:00
|
|
|
}
|
2021-10-05 13:07:55 +00:00
|
|
|
|
|
|
|
impl Decodable for Witness {
|
2022-06-29 01:22:12 +00:00
|
|
|
fn consensus_decode<R: Read + ?Sized>(r: &mut R) -> Result<Self, Error> {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
let witness_elements = VarInt::consensus_decode(r)?.0 as usize;
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
// 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 {
|
|
|
|
return Err(self::Error::OversizedVectorAllocation {
|
|
|
|
requested: witness_elements,
|
|
|
|
max: MAX_VEC_SIZE,
|
|
|
|
});
|
|
|
|
}
|
2021-10-05 13:07:55 +00:00
|
|
|
if witness_elements == 0 {
|
|
|
|
Ok(Witness::default())
|
|
|
|
} else {
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
// Leave space at the head for element positions.
|
|
|
|
// We will rotate them to the end of the Vec later.
|
2022-11-06 18:34:38 +00:00
|
|
|
let witness_index_space = witness_elements * 4;
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
let mut cursor = witness_index_space;
|
2021-10-05 13:07:55 +00:00
|
|
|
|
|
|
|
// this number should be determined as high enough to cover most witness, and low enough
|
|
|
|
// to avoid wasting space without reallocating
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
let mut content = vec![0u8; cursor + 128];
|
2021-10-05 13:07:55 +00:00
|
|
|
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
for i in 0..witness_elements {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
let element_size_varint = VarInt::consensus_decode(r)?;
|
2023-09-17 21:51:41 +00:00
|
|
|
let element_size_varint_len = element_size_varint.size();
|
2021-10-05 13:07:55 +00:00
|
|
|
let element_size = element_size_varint.0 as usize;
|
|
|
|
let required_len = cursor
|
|
|
|
.checked_add(element_size)
|
2022-05-25 03:28:31 +00:00
|
|
|
.ok_or(self::Error::OversizedVectorAllocation {
|
2023-05-02 22:16:20 +00:00
|
|
|
requested: usize::MAX,
|
2021-10-05 13:07:55 +00:00
|
|
|
max: MAX_VEC_SIZE,
|
|
|
|
})?
|
|
|
|
.checked_add(element_size_varint_len)
|
2022-05-25 03:28:31 +00:00
|
|
|
.ok_or(self::Error::OversizedVectorAllocation {
|
2023-05-02 22:16:20 +00:00
|
|
|
requested: usize::MAX,
|
2021-10-05 13:07:55 +00:00
|
|
|
max: MAX_VEC_SIZE,
|
|
|
|
})?;
|
|
|
|
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
if required_len > MAX_VEC_SIZE + witness_index_space {
|
2021-10-05 13:07:55 +00:00
|
|
|
return Err(self::Error::OversizedVectorAllocation {
|
|
|
|
requested: required_len,
|
|
|
|
max: MAX_VEC_SIZE,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
// We will do content.rotate_left(witness_index_space) later.
|
|
|
|
// Encode the position's value AFTER we rotate left.
|
|
|
|
encode_cursor(&mut content, 0, i, cursor - witness_index_space);
|
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
resize_if_needed(&mut content, required_len);
|
2023-02-21 23:01:26 +00:00
|
|
|
element_size_varint.consensus_encode(
|
|
|
|
&mut &mut content[cursor..cursor + element_size_varint_len],
|
|
|
|
)?;
|
2021-10-05 13:07:55 +00:00
|
|
|
cursor += element_size_varint_len;
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
r.read_exact(&mut content[cursor..cursor + element_size])?;
|
2021-10-05 13:07:55 +00:00
|
|
|
cursor += element_size;
|
|
|
|
}
|
|
|
|
content.truncate(cursor);
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
// Index space is now at the end of the Vec
|
|
|
|
content.rotate_left(witness_index_space);
|
2023-02-21 23:01:26 +00:00
|
|
|
Ok(Witness { content, witness_elements, indices_start: cursor - witness_index_space })
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-06 18:34:38 +00:00
|
|
|
/// Correctness Requirements: value must always fit within u32
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
#[inline]
|
|
|
|
fn encode_cursor(bytes: &mut [u8], start_of_indices: usize, index: usize, value: usize) {
|
2022-11-06 18:34:38 +00:00
|
|
|
let start = start_of_indices + index * 4;
|
|
|
|
let end = start + 4;
|
2023-02-21 23:01:26 +00:00
|
|
|
bytes[start..end]
|
|
|
|
.copy_from_slice(&u32::to_ne_bytes(value.try_into().expect("Larger than u32")));
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn decode_cursor(bytes: &[u8], start_of_indices: usize, index: usize) -> Option<usize> {
|
2022-11-06 18:34:38 +00:00
|
|
|
let start = start_of_indices + index * 4;
|
|
|
|
let end = start + 4;
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
if end > bytes.len() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(u32::from_ne_bytes(bytes[start..end].try_into().expect("is u32 size")) as usize)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
fn resize_if_needed(vec: &mut Vec<u8>, required_len: usize) {
|
|
|
|
if required_len >= vec.len() {
|
|
|
|
let mut new_len = vec.len().max(1);
|
|
|
|
while new_len <= required_len {
|
|
|
|
new_len *= 2;
|
|
|
|
}
|
|
|
|
vec.resize(new_len, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Encodable for Witness {
|
2022-06-29 01:22:12 +00:00
|
|
|
fn consensus_encode<W: Write + ?Sized>(&self, w: &mut W) -> Result<usize, io::Error> {
|
2023-08-24 00:37:53 +00:00
|
|
|
let len = VarInt::from(self.witness_elements);
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
len.consensus_encode(w)?;
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
let content_with_indices_len = self.content.len();
|
2022-11-06 18:34:38 +00:00
|
|
|
let indices_size = self.witness_elements * 4;
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
let content_len = content_with_indices_len - indices_size;
|
|
|
|
w.emit_slice(&self.content[..content_len])?;
|
2023-09-17 21:51:41 +00:00
|
|
|
Ok(content_len + len.size())
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Witness {
|
2022-10-31 01:23:10 +00:00
|
|
|
/// Creates a new empty [`Witness`].
|
2023-02-21 23:01:26 +00:00
|
|
|
pub fn new() -> Self { Witness::default() }
|
2022-01-11 02:03:06 +00:00
|
|
|
|
2023-09-20 05:03:25 +00:00
|
|
|
/// Creates a witness required to spend a P2WPKH output.
|
|
|
|
///
|
|
|
|
/// The witness will be made up of the DER encoded signature + sighash_type followed by the
|
|
|
|
/// serialized public key. Also useful for spending a P2SH-P2WPKH output.
|
|
|
|
///
|
|
|
|
/// It is expected that `pubkey` is related to the secret key used to create `signature`.
|
|
|
|
pub fn p2wpkh(signature: &ecdsa::Signature, pubkey: &secp256k1::PublicKey) -> Witness {
|
|
|
|
let mut witness = Witness::new();
|
|
|
|
witness.push_slice(&signature.serialize());
|
|
|
|
witness.push_slice(&pubkey.serialize());
|
|
|
|
witness
|
|
|
|
}
|
|
|
|
|
2022-11-14 17:50:17 +00:00
|
|
|
/// Creates a [`Witness`] object from a slice of bytes slices where each slice is a witness item.
|
|
|
|
pub fn from_slice<T: AsRef<[u8]>>(slice: &[T]) -> Self {
|
|
|
|
let witness_elements = slice.len();
|
|
|
|
let index_size = witness_elements * 4;
|
|
|
|
let content_size = slice
|
2021-10-05 13:07:55 +00:00
|
|
|
.iter()
|
2023-09-17 21:51:41 +00:00
|
|
|
.map(|elem| elem.as_ref().len() + VarInt::from(elem.as_ref().len()).size())
|
2021-10-05 13:07:55 +00:00
|
|
|
.sum();
|
2022-11-14 17:50:17 +00:00
|
|
|
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
let mut content = vec![0u8; content_size + index_size];
|
2021-10-05 13:07:55 +00:00
|
|
|
let mut cursor = 0usize;
|
2022-11-14 17:50:17 +00:00
|
|
|
for (i, elem) in slice.iter().enumerate() {
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
encode_cursor(&mut content, content_size, i, cursor);
|
2023-08-24 00:37:53 +00:00
|
|
|
let elem_len_varint = VarInt::from(elem.as_ref().len());
|
2022-11-14 17:50:17 +00:00
|
|
|
elem_len_varint
|
2023-09-17 21:51:41 +00:00
|
|
|
.consensus_encode(&mut &mut content[cursor..cursor + elem_len_varint.size()])
|
2021-10-05 13:07:55 +00:00
|
|
|
.expect("writers on vec don't errors, space granted by content_size");
|
2023-09-17 21:51:41 +00:00
|
|
|
cursor += elem_len_varint.size();
|
2022-11-14 17:50:17 +00:00
|
|
|
content[cursor..cursor + elem.as_ref().len()].copy_from_slice(elem.as_ref());
|
|
|
|
cursor += elem.as_ref().len();
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
2023-02-21 23:01:26 +00:00
|
|
|
Witness { witness_elements, content, indices_start: content_size }
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// Convenience method to create an array of byte-arrays from this witness.
|
2023-02-21 23:01:26 +00:00
|
|
|
pub fn to_vec(&self) -> Vec<Vec<u8>> { self.iter().map(|s| s.to_vec()).collect() }
|
2021-10-05 13:07:55 +00:00
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// Returns `true` if the witness contains no element.
|
2023-02-21 23:01:26 +00:00
|
|
|
pub fn is_empty(&self) -> bool { self.witness_elements == 0 }
|
2021-10-05 13:07:55 +00:00
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// Returns a struct implementing [`Iterator`].
|
2021-10-05 13:07:55 +00:00
|
|
|
pub fn iter(&self) -> Iter {
|
2023-02-21 23:01:26 +00:00
|
|
|
Iter { inner: self.content.as_slice(), indices_start: self.indices_start, current_index: 0 }
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// Returns the number of elements this witness holds.
|
2023-02-21 23:01:26 +00:00
|
|
|
pub fn len(&self) -> usize { self.witness_elements }
|
2021-10-05 13:07:55 +00:00
|
|
|
|
2023-09-17 21:51:41 +00:00
|
|
|
/// Returns the number of bytes this witness contributes to a transactions total size.
|
|
|
|
pub fn size(&self) -> usize {
|
|
|
|
let mut size: usize = 0;
|
|
|
|
|
|
|
|
size += VarInt::from(self.witness_elements).size();
|
|
|
|
size += self
|
|
|
|
.iter()
|
|
|
|
.map(|witness_element| {
|
|
|
|
VarInt::from(witness_element.len()).size() + witness_element.len()
|
|
|
|
})
|
|
|
|
.sum::<usize>();
|
|
|
|
|
|
|
|
size
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// Clear the witness.
|
2021-10-05 13:07:55 +00:00
|
|
|
pub fn clear(&mut self) {
|
|
|
|
self.content.clear();
|
|
|
|
self.witness_elements = 0;
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
self.indices_start = 0;
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// Push a new element on the witness, requires an allocation.
|
2021-10-05 13:07:55 +00:00
|
|
|
pub fn push<T: AsRef<[u8]>>(&mut self, new_element: T) {
|
2022-11-13 07:29:17 +00:00
|
|
|
self.push_slice(new_element.as_ref());
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Push a new element slice onto the witness stack.
|
|
|
|
fn push_slice(&mut self, new_element: &[u8]) {
|
2021-10-05 13:07:55 +00:00
|
|
|
self.witness_elements += 1;
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
let previous_content_end = self.indices_start;
|
2023-08-24 00:37:53 +00:00
|
|
|
let element_len_varint = VarInt::from(new_element.len());
|
2021-10-05 13:07:55 +00:00
|
|
|
let current_content_len = self.content.len();
|
2023-09-17 21:51:41 +00:00
|
|
|
let new_item_total_len = element_len_varint.size() + new_element.len();
|
2023-02-21 23:01:26 +00:00
|
|
|
self.content.resize(current_content_len + new_item_total_len + 4, 0);
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
|
|
|
|
self.content[previous_content_end..].rotate_right(new_item_total_len);
|
|
|
|
self.indices_start += new_item_total_len;
|
2023-02-21 23:01:26 +00:00
|
|
|
encode_cursor(
|
|
|
|
&mut self.content,
|
|
|
|
self.indices_start,
|
|
|
|
self.witness_elements - 1,
|
|
|
|
previous_content_end,
|
|
|
|
);
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
|
2023-09-17 21:51:41 +00:00
|
|
|
let end_varint = previous_content_end + element_len_varint.size();
|
2021-10-05 13:07:55 +00:00
|
|
|
element_len_varint
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
.consensus_encode(&mut &mut self.content[previous_content_end..end_varint])
|
2021-10-05 13:07:55 +00:00
|
|
|
.expect("writers on vec don't error, space granted through previous resize");
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
self.content[end_varint..end_varint + new_element.len()].copy_from_slice(new_element);
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
2023-09-21 03:35:12 +00:00
|
|
|
/// Pushes, as a new element on the witness, an ECDSA signature.
|
|
|
|
///
|
|
|
|
/// Pushes the DER encoded signature + sighash_type, requires an allocation.
|
|
|
|
pub fn push_ecdsa_signature(&mut self, signature: &ecdsa::Signature) {
|
|
|
|
self.push_slice(&signature.serialize())
|
|
|
|
}
|
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
fn element_at(&self, index: usize) -> Option<&[u8]> {
|
Take Writer/Reader by `&mut` in consensus en/decoding
Fix #1020 (see more relevant discussion there)
This definitely makes the amount of generics compiler
has to generate by avoding generating the same functions
for `R`, &mut R`, `&mut &mut R` and so on.
old:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9947832 Jun 2 22:42 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4463024 Jun 2 22:46 target/release/deps/bitcoin-07a9dabf1f3e0266
```
new:
```
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 9866800 Jun 2 22:44 target/release/deps/bitcoin-07a9dabf1f3e0266
> strip target/release/deps/bitcoin-07a9dabf1f3e0266
> ls -al target/release/deps/bitcoin-07a9dabf1f3e0266
-rwxrwxr-x 1 dpc dpc 4393392 Jun 2 22:45 target/release/deps/bitcoin-07a9dabf1f3e0266
```
In the unit-test binary itself, it saves ~100KB of data.
I did not expect much performance gains, but turn out I was wrong(*):
old:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,072,710 ns/iter (+/- 21,871)
test blockdata::block::benches::bench_block_serialize ... bench: 191,223 ns/iter (+/- 5,833)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 37,543 ns/iter (+/- 732)
test blockdata::block::benches::bench_stream_reader ... bench: 1,872,455 ns/iter (+/- 149,519)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 136 ns/iter (+/- 3)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 51 ns/iter (+/- 8)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 3 ns/iter (+/- 0)
```
new:
```
test blockdata::block::benches::bench_block_deserialize ... bench: 1,028,574 ns/iter (+/- 10,910)
test blockdata::block::benches::bench_block_serialize ... bench: 162,143 ns/iter (+/- 3,363)
test blockdata::block::benches::bench_block_serialize_logic ... bench: 30,725 ns/iter (+/- 695)
test blockdata::block::benches::bench_stream_reader ... bench: 1,437,071 ns/iter (+/- 53,694)
test blockdata::transaction::benches::bench_transaction_deserialize ... bench: 92 ns/iter (+/- 2)
test blockdata::transaction::benches::bench_transaction_serialize ... bench: 17 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_serialize_logic ... bench: 5 ns/iter (+/- 0)
test blockdata::transaction::benches::bench_transaction_size ... bench: 4 ns/iter (+/- 0)
```
(*) - I'm benchmarking on a noisy laptop. Take this with a grain of salt. But I think
at least it doesn't make anything slower.
While doing all this manual labor that will probably generate conflicts,
I took a liberty of changing generic type names and variable names to
`r` and `R` (reader) and `w` and `W` for writer.
2022-06-03 04:50:42 +00:00
|
|
|
let varint = VarInt::consensus_decode(&mut &self.content[index..]).ok()?;
|
2023-09-17 21:51:41 +00:00
|
|
|
let start = index + varint.size();
|
2021-10-05 13:07:55 +00:00
|
|
|
Some(&self.content[start..start + varint.0 as usize])
|
|
|
|
}
|
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// Returns the last element in the witness, if any.
|
2021-10-05 13:07:55 +00:00
|
|
|
pub fn last(&self) -> Option<&[u8]> {
|
|
|
|
if self.witness_elements == 0 {
|
|
|
|
None
|
|
|
|
} else {
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
self.nth(self.witness_elements - 1)
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-31 01:23:10 +00:00
|
|
|
/// Returns the second-to-last element in the witness, if any.
|
2021-10-05 13:07:55 +00:00
|
|
|
pub fn second_to_last(&self) -> Option<&[u8]> {
|
|
|
|
if self.witness_elements <= 1 {
|
|
|
|
None
|
|
|
|
} else {
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
self.nth(self.witness_elements - 2)
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
|
|
|
|
/// Return the nth element in the witness, if any
|
|
|
|
pub fn nth(&self, index: usize) -> Option<&[u8]> {
|
|
|
|
let pos = decode_cursor(&self.content, self.indices_start, index)?;
|
|
|
|
self.element_at(pos)
|
|
|
|
}
|
2022-11-03 01:31:17 +00:00
|
|
|
|
|
|
|
/// Get Tapscript following BIP341 rules regarding accounting for an annex.
|
|
|
|
///
|
2022-12-18 00:32:14 +00:00
|
|
|
/// This does not guarantee that this represents a P2TR [`Witness`]. It
|
|
|
|
/// merely gets the second to last or third to last element depending on
|
|
|
|
/// the first byte of the last element being equal to 0x50. See
|
2023-11-03 22:57:55 +00:00
|
|
|
/// [Script::is_p2tr](crate::blockdata::script::Script::is_p2tr) to
|
2022-12-18 00:32:14 +00:00
|
|
|
/// check whether this is actually a Taproot witness.
|
2022-12-29 14:22:16 +00:00
|
|
|
pub fn tapscript(&self) -> Option<&Script> {
|
2022-11-03 01:31:17 +00:00
|
|
|
let len = self.len();
|
2023-02-21 23:01:26 +00:00
|
|
|
self.last()
|
2022-11-03 01:31:17 +00:00
|
|
|
.map(|last_elem| {
|
|
|
|
// From BIP341:
|
|
|
|
// If there are at least two witness elements, and the first byte of
|
|
|
|
// the last element is 0x50, this last element is called annex a
|
|
|
|
// and is removed from the witness stack.
|
2022-11-12 10:02:13 +00:00
|
|
|
if len >= 2 && last_elem.first() == Some(&TAPROOT_ANNEX_PREFIX) {
|
2022-11-03 01:31:17 +00:00
|
|
|
// account for the extra item removed from the end
|
|
|
|
3
|
|
|
|
} else {
|
|
|
|
// otherwise script is 2nd from last
|
|
|
|
2
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.filter(|&script_pos_from_last| len >= script_pos_from_last)
|
2023-02-21 23:01:26 +00:00
|
|
|
.and_then(|script_pos_from_last| self.nth(len - script_pos_from_last))
|
2022-12-29 14:22:16 +00:00
|
|
|
.map(Script::from_bytes)
|
2022-11-03 01:31:17 +00:00
|
|
|
}
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Index<usize> for Witness {
|
|
|
|
type Output = [u8];
|
|
|
|
|
2023-02-21 23:01:26 +00:00
|
|
|
fn index(&self, index: usize) -> &Self::Output { self.nth(index).expect("Out of Bounds") }
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Iterator for Iter<'a> {
|
|
|
|
type Item = &'a [u8];
|
|
|
|
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
let index = decode_cursor(self.inner, self.indices_start, self.current_index)?;
|
|
|
|
let varint = VarInt::consensus_decode(&mut &self.inner[index..]).ok()?;
|
2023-09-17 21:51:41 +00:00
|
|
|
let start = index + varint.size();
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
let end = start + varint.0 as usize;
|
|
|
|
let slice = &self.inner[start..end];
|
|
|
|
self.current_index += 1;
|
2021-10-05 13:07:55 +00:00
|
|
|
Some(slice)
|
|
|
|
}
|
2022-06-13 13:39:34 +00:00
|
|
|
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
2022-11-06 18:34:38 +00:00
|
|
|
let total_count = (self.inner.len() - self.indices_start) / 4;
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
let remaining = total_count - self.current_index;
|
|
|
|
(remaining, Some(remaining))
|
2022-06-13 13:39:34 +00:00
|
|
|
}
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
2022-06-13 13:39:34 +00:00
|
|
|
impl<'a> ExactSizeIterator for Iter<'a> {}
|
|
|
|
|
2022-10-28 11:07:08 +00:00
|
|
|
impl<'a> IntoIterator for &'a Witness {
|
|
|
|
type IntoIter = Iter<'a>;
|
|
|
|
type Item = &'a [u8];
|
|
|
|
|
2023-02-21 23:01:26 +00:00
|
|
|
fn into_iter(self) -> Self::IntoIter { self.iter() }
|
2022-10-28 11:07:08 +00:00
|
|
|
}
|
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
// Serde keep backward compatibility with old Vec<Vec<u8>> format
|
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
impl serde::Serialize for Witness {
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
where
|
|
|
|
S: serde::Serializer,
|
|
|
|
{
|
2022-03-22 08:56:09 +00:00
|
|
|
use serde::ser::SerializeSeq;
|
2022-06-28 14:32:39 +00:00
|
|
|
|
2022-03-22 08:56:09 +00:00
|
|
|
let human_readable = serializer.is_human_readable();
|
2022-06-28 14:32:39 +00:00
|
|
|
let mut seq = serializer.serialize_seq(Some(self.witness_elements))?;
|
|
|
|
|
|
|
|
for elem in self.iter() {
|
2022-03-22 08:56:09 +00:00
|
|
|
if human_readable {
|
2023-01-07 15:39:11 +00:00
|
|
|
seq.serialize_element(&crate::serde_utils::SerializeBytesAsHex(elem))?;
|
2022-03-22 08:56:09 +00:00
|
|
|
} else {
|
|
|
|
seq.serialize_element(&elem)?;
|
|
|
|
}
|
2022-06-28 14:32:39 +00:00
|
|
|
}
|
|
|
|
seq.end()
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-28 05:27:23 +00:00
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
impl<'de> serde::Deserialize<'de> for Witness {
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
where
|
|
|
|
D: serde::Deserializer<'de>,
|
|
|
|
{
|
2023-02-21 23:01:26 +00:00
|
|
|
struct Visitor; // Human-readable visitor.
|
|
|
|
impl<'de> serde::de::Visitor<'de> for Visitor {
|
2022-03-22 08:56:09 +00:00
|
|
|
type Value = Witness;
|
|
|
|
|
|
|
|
fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
|
|
|
write!(f, "a sequence of hex arrays")
|
|
|
|
}
|
|
|
|
|
2023-02-21 23:01:26 +00:00
|
|
|
fn visit_seq<A: serde::de::SeqAccess<'de>>(
|
|
|
|
self,
|
|
|
|
mut a: A,
|
|
|
|
) -> Result<Self::Value, A::Error> {
|
2023-07-21 00:38:34 +00:00
|
|
|
use hex::FromHex;
|
|
|
|
use hex::HexToBytesError::*;
|
2022-03-22 08:56:09 +00:00
|
|
|
use serde::de::{self, Unexpected};
|
|
|
|
|
|
|
|
let mut ret = match a.size_hint() {
|
|
|
|
Some(len) => Vec::with_capacity(len),
|
|
|
|
None => Vec::new(),
|
|
|
|
};
|
|
|
|
|
|
|
|
while let Some(elem) = a.next_element::<String>()? {
|
2023-02-21 23:01:26 +00:00
|
|
|
let vec = Vec::<u8>::from_hex(&elem).map_err(|e| match e {
|
|
|
|
InvalidChar(b) => match core::char::from_u32(b.into()) {
|
|
|
|
Some(c) => de::Error::invalid_value(
|
|
|
|
Unexpected::Char(c),
|
|
|
|
&"a valid hex character",
|
|
|
|
),
|
|
|
|
None => de::Error::invalid_value(
|
|
|
|
Unexpected::Unsigned(b.into()),
|
|
|
|
&"a valid hex character",
|
|
|
|
),
|
|
|
|
},
|
|
|
|
OddLengthString(len) =>
|
|
|
|
de::Error::invalid_length(len, &"an even length string"),
|
2022-03-22 08:56:09 +00:00
|
|
|
})?;
|
|
|
|
ret.push(vec);
|
|
|
|
}
|
2022-11-14 17:50:17 +00:00
|
|
|
Ok(Witness::from_slice(&ret))
|
2022-03-22 08:56:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if deserializer.is_human_readable() {
|
|
|
|
deserializer.deserialize_seq(Visitor)
|
|
|
|
} else {
|
|
|
|
let vec: Vec<Vec<u8>> = serde::Deserialize::deserialize(deserializer)?;
|
2022-11-14 17:50:17 +00:00
|
|
|
Ok(Witness::from_slice(&vec))
|
2022-03-22 08:56:09 +00:00
|
|
|
}
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-11-15 00:54:37 +00:00
|
|
|
impl From<Vec<Vec<u8>>> for Witness {
|
2023-02-21 23:01:26 +00:00
|
|
|
fn from(vec: Vec<Vec<u8>>) -> Self { Witness::from_slice(&vec) }
|
2022-11-15 00:54:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&[&[u8]]> for Witness {
|
2023-02-21 23:01:26 +00:00
|
|
|
fn from(slice: &[&[u8]]) -> Self { Witness::from_slice(slice) }
|
2022-11-15 00:54:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<&[Vec<u8>]> for Witness {
|
2023-02-21 23:01:26 +00:00
|
|
|
fn from(slice: &[Vec<u8>]) -> Self { Witness::from_slice(slice) }
|
2022-11-15 00:54:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Vec<&[u8]>> for Witness {
|
2023-02-21 23:01:26 +00:00
|
|
|
fn from(vec: Vec<&[u8]>) -> Self { Witness::from_slice(&vec) }
|
2022-11-15 00:54:37 +00:00
|
|
|
}
|
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2023-08-10 22:44:58 +00:00
|
|
|
use hex::test_hex_unwrap as hex;
|
|
|
|
|
2022-05-02 22:13:57 +00:00
|
|
|
use super::*;
|
|
|
|
use crate::consensus::{deserialize, serialize};
|
2023-11-03 22:57:55 +00:00
|
|
|
use crate::sighash::EcdsaSighashType;
|
2023-02-21 23:01:26 +00:00
|
|
|
use crate::Transaction;
|
2021-10-05 13:07:55 +00:00
|
|
|
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
fn append_u32_vec(mut v: Vec<u8>, n: &[u32]) -> Vec<u8> {
|
|
|
|
for &num in n {
|
2022-11-06 18:34:38 +00:00
|
|
|
v.extend_from_slice(&num.to_ne_bytes());
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
}
|
|
|
|
v
|
|
|
|
}
|
|
|
|
|
2023-08-16 06:03:33 +00:00
|
|
|
#[test]
|
|
|
|
fn witness_debug_can_display_empty_instruction() {
|
|
|
|
let witness = Witness {
|
|
|
|
witness_elements: 1,
|
|
|
|
content: append_u32_vec(vec![], &[0]),
|
|
|
|
indices_start: 2,
|
|
|
|
};
|
|
|
|
println!("{:?}", witness);
|
|
|
|
}
|
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
#[test]
|
|
|
|
fn test_push() {
|
|
|
|
let mut witness = Witness::default();
|
|
|
|
assert_eq!(witness.last(), None);
|
|
|
|
assert_eq!(witness.second_to_last(), None);
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
assert_eq!(witness.nth(0), None);
|
|
|
|
assert_eq!(witness.nth(1), None);
|
|
|
|
assert_eq!(witness.nth(2), None);
|
|
|
|
assert_eq!(witness.nth(3), None);
|
2021-10-05 13:07:55 +00:00
|
|
|
witness.push(&vec![0u8]);
|
|
|
|
let expected = Witness {
|
|
|
|
witness_elements: 1,
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
content: append_u32_vec(vec![1u8, 0], &[0]),
|
|
|
|
indices_start: 2,
|
2021-10-05 13:07:55 +00:00
|
|
|
};
|
|
|
|
assert_eq!(witness, expected);
|
|
|
|
assert_eq!(witness.last(), Some(&[0u8][..]));
|
|
|
|
assert_eq!(witness.second_to_last(), None);
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
assert_eq!(witness.nth(0), Some(&[0u8][..]));
|
|
|
|
assert_eq!(witness.nth(1), None);
|
|
|
|
assert_eq!(witness.nth(2), None);
|
|
|
|
assert_eq!(witness.nth(3), None);
|
|
|
|
assert_eq!(&witness[0], &[0u8][..]);
|
2021-10-05 13:07:55 +00:00
|
|
|
witness.push(&vec![2u8, 3u8]);
|
|
|
|
let expected = Witness {
|
|
|
|
witness_elements: 2,
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
content: append_u32_vec(vec![1u8, 0, 2, 2, 3], &[0, 2]),
|
|
|
|
indices_start: 5,
|
2021-10-05 13:07:55 +00:00
|
|
|
};
|
|
|
|
assert_eq!(witness, expected);
|
|
|
|
assert_eq!(witness.last(), Some(&[2u8, 3u8][..]));
|
|
|
|
assert_eq!(witness.second_to_last(), Some(&[0u8][..]));
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
assert_eq!(witness.nth(0), Some(&[0u8][..]));
|
|
|
|
assert_eq!(witness.nth(1), Some(&[2u8, 3u8][..]));
|
|
|
|
assert_eq!(witness.nth(2), None);
|
|
|
|
assert_eq!(witness.nth(3), None);
|
|
|
|
assert_eq!(&witness[0], &[0u8][..]);
|
|
|
|
assert_eq!(&witness[1], &[2u8, 3u8][..]);
|
|
|
|
witness.push(&vec![4u8, 5u8]);
|
|
|
|
let expected = Witness {
|
|
|
|
witness_elements: 3,
|
|
|
|
content: append_u32_vec(vec![1u8, 0, 2, 2, 3, 2, 4, 5], &[0, 2, 5]),
|
|
|
|
indices_start: 8,
|
|
|
|
};
|
|
|
|
assert_eq!(witness, expected);
|
|
|
|
assert_eq!(witness.last(), Some(&[4u8, 5u8][..]));
|
|
|
|
assert_eq!(witness.second_to_last(), Some(&[2u8, 3u8][..]));
|
|
|
|
assert_eq!(witness.nth(0), Some(&[0u8][..]));
|
|
|
|
assert_eq!(witness.nth(1), Some(&[2u8, 3u8][..]));
|
|
|
|
assert_eq!(witness.nth(2), Some(&[4u8, 5u8][..]));
|
|
|
|
assert_eq!(witness.nth(3), None);
|
|
|
|
assert_eq!(&witness[0], &[0u8][..]);
|
|
|
|
assert_eq!(&witness[1], &[2u8, 3u8][..]);
|
|
|
|
assert_eq!(&witness[2], &[4u8, 5u8][..]);
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
|
|
|
|
2022-06-13 13:39:34 +00:00
|
|
|
#[test]
|
|
|
|
fn test_iter_len() {
|
|
|
|
let mut witness = Witness::default();
|
|
|
|
for i in 0..5 {
|
|
|
|
assert_eq!(witness.iter().len(), i);
|
|
|
|
witness.push(&vec![0u8]);
|
|
|
|
}
|
|
|
|
let mut iter = witness.iter();
|
|
|
|
for i in (0..=5).rev() {
|
|
|
|
assert_eq!(iter.len(), i);
|
|
|
|
iter.next();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-05 03:27:28 +00:00
|
|
|
#[test]
|
|
|
|
fn test_push_ecdsa_sig() {
|
|
|
|
// The very first signature in block 734,958
|
|
|
|
let sig_bytes =
|
2022-12-03 19:57:18 +00:00
|
|
|
hex!("304402207c800d698f4b0298c5aac830b822f011bb02df41eb114ade9a6702f364d5e39c0220366900d2a60cab903e77ef7dd415d46509b1f78ac78906e3296f495aa1b1b541");
|
2023-09-21 03:35:12 +00:00
|
|
|
let sig = secp256k1::ecdsa::Signature::from_der(&sig_bytes).unwrap();
|
2022-05-05 03:27:28 +00:00
|
|
|
let mut witness = Witness::default();
|
2023-08-10 22:44:58 +00:00
|
|
|
let signature = crate::ecdsa::Signature { sig, hash_ty: EcdsaSighashType::All };
|
2023-09-21 03:35:12 +00:00
|
|
|
witness.push_ecdsa_signature(&signature);
|
2022-12-03 19:57:18 +00:00
|
|
|
let expected_witness = vec![hex!(
|
2022-05-05 03:27:28 +00:00
|
|
|
"304402207c800d698f4b0298c5aac830b822f011bb02df41eb114ade9a6702f364d5e39c0220366900d2a60cab903e77ef7dd415d46509b1f78ac78906e3296f495aa1b1b54101")
|
2022-12-03 19:57:18 +00:00
|
|
|
];
|
2022-05-05 03:27:28 +00:00
|
|
|
assert_eq!(witness.to_vec(), expected_witness);
|
|
|
|
}
|
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
#[test]
|
|
|
|
fn test_witness() {
|
2023-02-21 23:01:26 +00:00
|
|
|
let w0 = hex!("03d2e15674941bad4a996372cb87e1856d3652606d98562fe39c5e9e7e413f2105");
|
2022-12-03 19:57:18 +00:00
|
|
|
let w1 = hex!("000000");
|
2021-10-05 13:07:55 +00:00
|
|
|
let witness_vec = vec![w0.clone(), w1.clone()];
|
|
|
|
let witness_serialized: Vec<u8> = serialize(&witness_vec);
|
|
|
|
let witness = Witness {
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
content: append_u32_vec(witness_serialized[1..].to_vec(), &[0, 34]),
|
2021-10-05 13:07:55 +00:00
|
|
|
witness_elements: 2,
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
indices_start: 38,
|
2021-10-05 13:07:55 +00:00
|
|
|
};
|
|
|
|
for (i, el) in witness.iter().enumerate() {
|
|
|
|
assert_eq!(witness_vec[i], el);
|
|
|
|
}
|
|
|
|
assert_eq!(witness.last(), Some(&w1[..]));
|
|
|
|
assert_eq!(witness.second_to_last(), Some(&w0[..]));
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
assert_eq!(witness.nth(0), Some(&w0[..]));
|
|
|
|
assert_eq!(witness.nth(1), Some(&w1[..]));
|
|
|
|
assert_eq!(witness.nth(2), None);
|
|
|
|
assert_eq!(&witness[0], &w0[..]);
|
|
|
|
assert_eq!(&witness[1], &w1[..]);
|
2021-10-05 13:07:55 +00:00
|
|
|
|
2022-11-14 17:50:17 +00:00
|
|
|
let w_into = Witness::from_slice(&witness_vec);
|
2021-10-05 13:07:55 +00:00
|
|
|
assert_eq!(w_into, witness);
|
|
|
|
|
|
|
|
assert_eq!(witness_serialized, serialize(&witness));
|
|
|
|
}
|
|
|
|
|
2022-11-03 01:31:17 +00:00
|
|
|
#[test]
|
|
|
|
fn test_get_tapscript() {
|
2022-12-03 19:57:18 +00:00
|
|
|
let tapscript = hex!("deadbeef");
|
|
|
|
let control_block = hex!("02");
|
2022-11-03 01:31:17 +00:00
|
|
|
// annex starting with 0x50 causes the branching logic.
|
2022-12-03 19:57:18 +00:00
|
|
|
let annex = hex!("50");
|
2022-11-03 01:31:17 +00:00
|
|
|
|
|
|
|
let witness_vec = vec![tapscript.clone(), control_block.clone()];
|
|
|
|
let witness_vec_annex = vec![tapscript.clone(), control_block, annex];
|
|
|
|
|
|
|
|
let witness_serialized: Vec<u8> = serialize(&witness_vec);
|
|
|
|
let witness_serialized_annex: Vec<u8> = serialize(&witness_vec_annex);
|
|
|
|
|
|
|
|
let witness = Witness {
|
|
|
|
content: append_u32_vec(witness_serialized[1..].to_vec(), &[0, 5]),
|
|
|
|
witness_elements: 2,
|
|
|
|
indices_start: 7,
|
|
|
|
};
|
|
|
|
let witness_annex = Witness {
|
|
|
|
content: append_u32_vec(witness_serialized_annex[1..].to_vec(), &[0, 5, 7]),
|
|
|
|
witness_elements: 3,
|
|
|
|
indices_start: 9,
|
|
|
|
};
|
|
|
|
|
|
|
|
// With or without annex, the tapscript should be returned.
|
2022-12-29 14:22:16 +00:00
|
|
|
assert_eq!(witness.tapscript(), Some(Script::from_bytes(&tapscript[..])));
|
|
|
|
assert_eq!(witness_annex.tapscript(), Some(Script::from_bytes(&tapscript[..])));
|
2022-11-03 01:31:17 +00:00
|
|
|
}
|
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
#[test]
|
|
|
|
fn test_tx() {
|
2022-12-03 19:57:18 +00:00
|
|
|
const S: &str = "02000000000102b44f26b275b8ad7b81146ba3dbecd081f9c1ea0dc05b97516f56045cfcd3df030100000000ffffffff1cb4749ae827c0b75f3d0a31e63efc8c71b47b5e3634a4c698cd53661cab09170100000000ffffffff020b3a0500000000001976a9143ea74de92762212c96f4dd66c4d72a4deb20b75788ac630500000000000016001493a8dfd1f0b6a600ab01df52b138cda0b82bb7080248304502210084622878c94f4c356ce49c8e33a063ec90f6ee9c0208540888cfab056cd1fca9022014e8dbfdfa46d318c6887afd92dcfa54510e057565e091d64d2ee3a66488f82c0121026e181ffb98ebfe5a64c983073398ea4bcd1548e7b971b4c175346a25a1c12e950247304402203ef00489a0d549114977df2820fab02df75bebb374f5eee9e615107121658cfa02204751f2d1784f8e841bff6d3bcf2396af2f1a5537c0e4397224873fbd3bfbe9cf012102ae6aa498ce2dd204e9180e71b4fb1260fe3d1a95c8025b34e56a9adf5f278af200000000";
|
|
|
|
let tx_bytes = hex!(S);
|
2021-10-05 13:07:55 +00:00
|
|
|
let tx: Transaction = deserialize(&tx_bytes).unwrap();
|
|
|
|
|
|
|
|
let expected_wit = ["304502210084622878c94f4c356ce49c8e33a063ec90f6ee9c0208540888cfab056cd1fca9022014e8dbfdfa46d318c6887afd92dcfa54510e057565e091d64d2ee3a66488f82c01", "026e181ffb98ebfe5a64c983073398ea4bcd1548e7b971b4c175346a25a1c12e95"];
|
|
|
|
for (i, wit_el) in tx.input[0].witness.iter().enumerate() {
|
2023-01-07 15:39:11 +00:00
|
|
|
assert_eq!(expected_wit[i], wit_el.to_lower_hex_string());
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
2023-01-07 15:39:11 +00:00
|
|
|
assert_eq!(expected_wit[1], tx.input[0].witness.last().unwrap().to_lower_hex_string());
|
2023-02-21 23:01:26 +00:00
|
|
|
assert_eq!(
|
|
|
|
expected_wit[0],
|
|
|
|
tx.input[0].witness.second_to_last().unwrap().to_lower_hex_string()
|
|
|
|
);
|
2023-01-07 15:39:11 +00:00
|
|
|
assert_eq!(expected_wit[0], tx.input[0].witness.nth(0).unwrap().to_lower_hex_string());
|
|
|
|
assert_eq!(expected_wit[1], tx.input[0].witness.nth(1).unwrap().to_lower_hex_string());
|
Add Index<usize> and nth(index) to Witness
Arbitrary indexing into Witness fixes the API of last and second_to_last to be more flexible.
This patch started off as an addition of third_to_last, but ended up evolving
into arbitrary indexing to allow for future use cases.
A list of the indices of the start byte for each witness element is stored as an ordered
contiguous group of u32s represented as 4 bytes each in the Vec<u8> contents.
The bytes are stored using to_ne_bytes for performance reasons. A helper function is added
to the tests to allow for easier contruction of the contents Vec in test vectors. u32 was
chosen because 22 bits are needed to store 4,000,000 which is the maximum weight limit for
a block. This might need to be reworked in the event of consensus limits increasing, but
u32 can hold 1000x the current limit, so it should be fine for the forseeable future.
The push and consensus_deserialize functions utilize rotate_left and rotate_right to move
the indices to the end of the new allocation. Depending on the size of the data, this
might be more of a performance hit than just allocating a new temporary Vec to store the
indices and append them after parsing is completed. However, for a majority of cases
rotating the indices should be faster. Suggestions to use VecDeque instead of Vec for
contents necessitate other considerations, since it is not a public facing change,
those optimizations can be dealt with in future patches.
The Index<usize> trait is implemented using the new nth method with expect.
The Iter struct is reworked to make use of the new data representation. This new data
structure makes it trivial to implement DoubleEndedIterator and other such traits, but
I have decided to leave this as out of scope for this patch.
2022-11-03 01:27:58 +00:00
|
|
|
assert_eq!(None, tx.input[0].witness.nth(2));
|
2023-01-07 15:39:11 +00:00
|
|
|
assert_eq!(expected_wit[0], tx.input[0].witness[0].to_lower_hex_string());
|
|
|
|
assert_eq!(expected_wit[1], tx.input[0].witness[1].to_lower_hex_string());
|
2022-01-24 00:31:39 +00:00
|
|
|
|
2021-10-05 13:07:55 +00:00
|
|
|
let tx_bytes_back = serialize(&tx);
|
|
|
|
assert_eq!(tx_bytes_back, tx_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn fuzz_cases() {
|
2022-12-03 19:57:18 +00:00
|
|
|
let bytes = hex!("26ff0000000000c94ce592cf7a4cbb68eb00ce374300000057cd0000000000000026");
|
2021-10-05 13:07:55 +00:00
|
|
|
assert!(deserialize::<Witness>(&bytes).is_err()); // OversizedVectorAllocation
|
|
|
|
|
2022-12-03 19:57:18 +00:00
|
|
|
let bytes = hex!("24000000ffffffffffffffffffffffff");
|
2021-10-05 13:07:55 +00:00
|
|
|
assert!(deserialize::<Witness>(&bytes).is_err()); // OversizedVectorAllocation
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
#[test]
|
2022-03-22 08:32:35 +00:00
|
|
|
fn test_serde_bincode() {
|
|
|
|
use bincode;
|
2021-10-05 13:07:55 +00:00
|
|
|
|
|
|
|
let old_witness_format = vec![vec![0u8], vec![2]];
|
2022-11-14 17:50:17 +00:00
|
|
|
let new_witness_format = Witness::from_slice(&old_witness_format);
|
2021-10-05 13:07:55 +00:00
|
|
|
|
2022-03-22 08:32:35 +00:00
|
|
|
let old = bincode::serialize(&old_witness_format).unwrap();
|
|
|
|
let new = bincode::serialize(&new_witness_format).unwrap();
|
2021-10-05 13:07:55 +00:00
|
|
|
|
|
|
|
assert_eq!(old, new);
|
|
|
|
|
2022-03-22 08:32:35 +00:00
|
|
|
let back: Witness = bincode::deserialize(&new).unwrap();
|
2021-10-05 13:07:55 +00:00
|
|
|
assert_eq!(new_witness_format, back);
|
|
|
|
}
|
2022-03-22 08:32:35 +00:00
|
|
|
|
2022-03-22 08:56:32 +00:00
|
|
|
#[cfg(feature = "serde")]
|
|
|
|
#[test]
|
|
|
|
fn test_serde_human() {
|
|
|
|
use serde_json;
|
|
|
|
|
2022-11-14 17:50:17 +00:00
|
|
|
let witness = Witness::from_slice(&[vec![0u8, 123, 75], vec![2u8, 6, 3, 7, 8]]);
|
2022-03-22 08:56:32 +00:00
|
|
|
|
|
|
|
let json = serde_json::to_string(&witness).unwrap();
|
|
|
|
|
|
|
|
assert_eq!(json, r#"["007b4b","0206030708"]"#);
|
|
|
|
|
|
|
|
let back: Witness = serde_json::from_str(&json).unwrap();
|
|
|
|
assert_eq!(witness, back);
|
|
|
|
}
|
2021-10-05 13:07:55 +00:00
|
|
|
}
|
2022-06-13 13:39:34 +00:00
|
|
|
|
2022-07-12 00:07:38 +00:00
|
|
|
#[cfg(bench)]
|
2022-06-13 13:39:34 +00:00
|
|
|
mod benches {
|
2023-02-21 23:01:26 +00:00
|
|
|
use test::{black_box, Bencher};
|
|
|
|
|
2022-06-13 13:39:34 +00:00
|
|
|
use super::Witness;
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
pub fn bench_big_witness_to_vec(bh: &mut Bencher) {
|
2022-11-14 17:50:17 +00:00
|
|
|
let raw_witness = [[1u8]; 5];
|
|
|
|
let witness = Witness::from_slice(&raw_witness);
|
2022-06-13 13:39:34 +00:00
|
|
|
|
|
|
|
bh.iter(|| {
|
|
|
|
black_box(witness.to_vec());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[bench]
|
|
|
|
pub fn bench_witness_to_vec(bh: &mut Bencher) {
|
|
|
|
let raw_witness = vec![vec![1u8]; 3];
|
2022-11-14 17:50:17 +00:00
|
|
|
let witness = Witness::from_slice(&raw_witness);
|
2022-06-13 13:39:34 +00:00
|
|
|
|
|
|
|
bh.iter(|| {
|
|
|
|
black_box(witness.to_vec());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|