Merge rust-bitcoin/rust-bitcoin#3406: Move `Witness` to `primitives`

c1eccfde25 Move Witness to primitives (Tobin C. Harding)
6ce76cd7c8 Add unstable constructor to Witness (Tobin C. Harding)

Pull request description:

  Patch 1 introduces a new policy to the codebase, we use `foo__unstable` for public unstable functions and there are zero semver guarantees if you call these functions.

  Patch 2 does the move.

  Close #3406

ACKs for top commit:
  apoelstra:
    ACK c1eccfde25fd4c2b19e7ec6759352b46ac246113; successfully ran local tests

Tree-SHA512: 2388066be2b6bb2cf3d6757c8f6334beeda6115ef1ce7c537955d32aa5e466add5162d0d2adee27f416fe622fc93c4e94bd848326463ee55e08d1c0f4e03719c
This commit is contained in:
merge-script 2024-10-18 16:35:15 +00:00
commit 51d503730d
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
14 changed files with 794 additions and 678 deletions

View File

@ -95,6 +95,7 @@ name = "bitcoin-internals"
version = "0.4.0"
dependencies = [
"bincode",
"hex-conservative",
"serde",
"serde_json",
]
@ -111,6 +112,7 @@ name = "bitcoin-primitives"
version = "0.100.0"
dependencies = [
"arbitrary",
"bincode",
"bitcoin-internals",
"bitcoin-io",
"bitcoin-units",
@ -119,6 +121,7 @@ dependencies = [
"mutagen",
"ordered",
"serde",
"serde_json",
]
[[package]]
@ -204,7 +207,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1aa273bf451e37ed35ced41c71a5e2a4e29064afb104158f2514bcd71c2c986"
dependencies = [
"arrayvec",
"serde",
]
[[package]]

View File

@ -94,6 +94,7 @@ name = "bitcoin-internals"
version = "0.4.0"
dependencies = [
"bincode",
"hex-conservative",
"serde",
"serde_json",
]
@ -110,6 +111,7 @@ name = "bitcoin-primitives"
version = "0.100.0"
dependencies = [
"arbitrary",
"bincode",
"bitcoin-internals",
"bitcoin-io",
"bitcoin-units",
@ -118,6 +120,7 @@ dependencies = [
"mutagen",
"ordered",
"serde",
"serde_json",
]
[[package]]
@ -206,7 +209,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
dependencies = [
"arrayvec",
"serde",
]
[[package]]

View File

@ -52,6 +52,13 @@ are no plans to do so. Of course, patches to fix specific consensus incompatibil
16-bit pointer sizes are not supported and we can't promise they will be. If you care about them
please let us know, so we can know how large the interest is and possibly decide to support them.
### Semver compliance
We try hard to maintain strict semver compliance with our releases. This codebase includes some
public functions marked unstable (e.g., `pub fn foo__unstable()`). These functions do not adhere to
semver rules; use them at your own discretion.
## Documentation
Currently can be found on [docs.rs/bitcoin](https://docs.rs/bitcoin/). Patches to add usage examples

View File

@ -29,6 +29,7 @@ use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, IntoDerivationPat
use bitcoin::locktime::absolute;
use bitcoin::psbt::Input;
use bitcoin::secp256k1::{Secp256k1, Signing};
use bitcoin::witness::WitnessExt as _;
use bitcoin::{
consensus, transaction, Address, Amount, EcdsaSighashType, Network, OutPoint, Psbt, ScriptBuf,
Sequence, Transaction, TxIn, TxOut, Txid, WPubkeyHash, Witness,

View File

@ -6,6 +6,7 @@ use bitcoin::address::script_pubkey::ScriptBufExt as _;
use bitcoin::locktime::absolute;
use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Signing};
use bitcoin::sighash::{EcdsaSighashType, SighashCache};
use bitcoin::witness::WitnessExt as _;
use bitcoin::{
transaction, Address, Amount, Network, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut,
Txid, WPubkeyHash, Witness,

View File

@ -7,6 +7,7 @@ use bitcoin::key::{Keypair, TapTweak, TweakedKeypair, UntweakedPublicKey};
use bitcoin::locktime::absolute;
use bitcoin::secp256k1::{rand, Message, Secp256k1, SecretKey, Signing, Verification};
use bitcoin::sighash::{Prevouts, SighashCache, TapSighashType};
use bitcoin::witness::WitnessExt as _;
use bitcoin::{
transaction, Address, Amount, Network, OutPoint, ScriptBuf, Sequence, Transaction, TxIn, TxOut,
Txid, Witness,

View File

@ -28,6 +28,7 @@ use bitcoin::key::UntweakedPublicKey;
use bitcoin::locktime::absolute;
use bitcoin::psbt::Input;
use bitcoin::secp256k1::{Secp256k1, Signing};
use bitcoin::witness::WitnessExt as _;
use bitcoin::{
consensus, transaction, Address, Amount, Network, OutPoint, Psbt, ScriptBuf, Sequence,
TapLeafHash, TapSighashType, Transaction, TxIn, TxOut, Txid, Witness, XOnlyPublicKey,

View File

@ -4,11 +4,6 @@
//!
//! This module contains the [`Witness`] struct and related methods to operate on it
use core::fmt;
use core::ops::Index;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use internals::compact_size;
use io::{BufRead, Write};
@ -21,119 +16,9 @@ use crate::script::ScriptExt as _;
use crate::taproot::{self, TAPROOT_ANNEX_PREFIX};
use crate::Script;
/// The Witness is the data used to unlock bitcoin since the [segwit upgrade].
///
/// 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`].
///
/// For serialization and deserialization performance it is stored internally as a single `Vec`,
/// saving some allocations.
///
/// [segwit upgrade]: <https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki>
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Witness {
/// Contains the witness `Vec<Vec<u8>>` serialization.
///
/// Does not include the initial varint indicating the number of elements. Each element however,
/// does include a varint indicating the element length. The number of elements is stored in
/// `witness_elements`.
///
/// Concatenated onto the end of `content` is the index area. This is a `4 * witness_elements`
/// bytes area which stores the index of the start of each witness item.
content: Vec<u8>,
/// The number of elements in the witness.
///
/// Stored separately (instead of as a compact size encoding in the initial part of content) so
/// that methods like [`Witness::push`] don't have to shift the entire array.
witness_elements: usize,
/// This is the valid index pointing to the beginning of the index area.
///
/// Said another way, this is the total length of all witness elements serialized (without the
/// element count but with their sizes serialized as compact size).
indices_start: usize,
}
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: { ")?;
write!(f, "indices: {}, ", w.witness_elements)?;
write!(f, "indices_start: {}, ", w.indices_start)?;
f.write_str("witnesses: [")?;
let instructions = w.iter();
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, "[]")?;
}
}
f.write_str(comma_or_close(i, last_instruction))?;
}
}
None => {
// Witnesses can be empty because the 0x00 var int is not stored in content.
write!(f, "]")?;
}
}
f.write_str(" }")
}
fn fmt_debug_pretty(w: &Witness, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
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 {
f.write_str(", ")?;
}
write!(f, "{:#04x}", byte)?;
}
f.write_str("],\n")?;
}
writeln!(f, " ],")?;
writeln!(f, "}}")
}
/// An iterator returning individual witness elements.
pub struct Iter<'a> {
inner: &'a [u8],
indices_start: usize,
current_index: usize,
}
#[rustfmt::skip] // Keep public re-exports separate.
#[doc(inline)]
pub use primitives::witness::{Witness, Iter};
impl Decodable for Witness {
fn consensus_decode<R: BufRead + ?Sized>(r: &mut R) -> Result<Self, Error> {
@ -193,31 +78,12 @@ impl Decodable for Witness {
content.truncate(cursor);
// Index space is now at the end of the Vec
content.rotate_left(witness_index_space);
Ok(Witness { content, witness_elements, indices_start: cursor - witness_index_space })
let indices_start = cursor - witness_index_space;
Ok(Witness::from_parts__unstable(content, witness_elements, indices_start))
}
}
}
/// Correctness Requirements: value must always fit within u32
#[inline]
fn encode_cursor(bytes: &mut [u8], start_of_indices: usize, index: usize, value: usize) {
let start = start_of_indices + index * 4;
let end = start + 4;
bytes[start..end]
.copy_from_slice(&u32::to_ne_bytes(value.try_into().expect("larger than u32")));
}
#[inline]
fn decode_cursor(bytes: &[u8], start_of_indices: usize, index: usize) -> Option<usize> {
let start = start_of_indices + index * 4;
let end = start + 4;
if end > bytes.len() {
None
} else {
Some(u32::from_ne_bytes(bytes[start..end].try_into().expect("is u32 size")) as usize)
}
}
fn resize_if_needed(vec: &mut Vec<u8>, required_len: usize) {
if required_len >= vec.len() {
let mut new_len = vec.len().max(1);
@ -241,12 +107,12 @@ impl Encodable for Witness {
}
}
impl Witness {
/// Creates a new empty [`Witness`].
#[inline]
pub const fn new() -> Self {
Witness { content: Vec::new(), witness_elements: 0, indices_start: 0 }
}
crate::internal_macros::define_extension_trait! {
/// Extension functionality for the [`Witness`] type.
pub trait WitnessExt impl for Witness {
/// Convenience method to create an array of byte-arrays from this witness.
#[deprecated(since = "TBD", note = "use `to_bytes` instead")]
fn to_vec(&self) -> Vec<Vec<u8>> { self.to_bytes() }
/// Creates a witness required to spend a P2WPKH output.
///
@ -254,7 +120,7 @@ impl Witness {
/// 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 {
fn p2wpkh(signature: ecdsa::Signature, pubkey: secp256k1::PublicKey) -> Witness {
let mut witness = Witness::new();
witness.push(signature.serialize());
witness.push(pubkey.serialize());
@ -262,149 +128,19 @@ impl Witness {
}
/// Creates a witness required to do a key path spend of a P2TR output.
pub fn p2tr_key_spend(signature: &taproot::Signature) -> Witness {
fn p2tr_key_spend(signature: &taproot::Signature) -> Witness {
let mut witness = Witness::new();
witness.push(signature.serialize());
witness
}
/// 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
.iter()
.map(|elem| elem.as_ref().len() + compact_size::encoded_size(elem.as_ref().len()))
.sum();
let mut content = vec![0u8; content_size + index_size];
let mut cursor = 0usize;
for (i, elem) in slice.iter().enumerate() {
encode_cursor(&mut content, content_size, i, cursor);
let encoded = compact_size::encode(elem.as_ref().len());
let encoded_size = encoded.as_slice().len();
content[cursor..cursor + encoded_size].copy_from_slice(encoded.as_slice());
cursor += encoded_size;
content[cursor..cursor + elem.as_ref().len()].copy_from_slice(elem.as_ref());
cursor += elem.as_ref().len();
}
Witness { witness_elements, content, indices_start: content_size }
}
/// Convenience method to create an array of byte-arrays from this witness.
pub fn to_bytes(&self) -> Vec<Vec<u8>> { self.iter().map(|s| s.to_vec()).collect() }
/// Convenience method to create an array of byte-arrays from this witness.
#[deprecated(since = "TBD", note = "use `to_bytes` instead")]
pub fn to_vec(&self) -> Vec<Vec<u8>> { self.to_bytes() }
/// Returns `true` if the witness contains no element.
pub fn is_empty(&self) -> bool { self.witness_elements == 0 }
/// Returns a struct implementing [`Iterator`].
pub fn iter(&self) -> Iter {
Iter { inner: self.content.as_slice(), indices_start: self.indices_start, current_index: 0 }
}
/// Returns the number of elements this witness holds.
pub fn len(&self) -> usize { self.witness_elements }
/// Returns the number of bytes this witness contributes to a transactions total size.
pub fn size(&self) -> usize {
let mut size: usize = 0;
size += compact_size::encoded_size(self.witness_elements);
size += self
.iter()
.map(|witness_element| {
let len = witness_element.len();
compact_size::encoded_size(len) + len
})
.sum::<usize>();
size
}
/// Clear the witness.
pub fn clear(&mut self) {
self.content.clear();
self.witness_elements = 0;
self.indices_start = 0;
}
/// Push a new element on the witness, requires an allocation.
pub fn push<T: AsRef<[u8]>>(&mut self, new_element: T) {
self.push_slice(new_element.as_ref());
}
/// Push a new element slice onto the witness stack.
fn push_slice(&mut self, new_element: &[u8]) {
self.witness_elements += 1;
let previous_content_end = self.indices_start;
let encoded = compact_size::encode(new_element.len());
let encoded_size = encoded.as_slice().len();
let current_content_len = self.content.len();
let new_item_total_len = encoded_size + new_element.len();
self.content.resize(current_content_len + new_item_total_len + 4, 0);
self.content[previous_content_end..].rotate_right(new_item_total_len);
self.indices_start += new_item_total_len;
encode_cursor(
&mut self.content,
self.indices_start,
self.witness_elements - 1,
previous_content_end,
);
let end_compact_size = previous_content_end + encoded_size;
self.content[previous_content_end..end_compact_size].copy_from_slice(encoded.as_slice());
self.content[end_compact_size..end_compact_size + new_element.len()]
.copy_from_slice(new_element);
}
/// 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) {
fn push_ecdsa_signature(&mut self, signature: ecdsa::Signature) {
self.push(signature.serialize())
}
/// Note `index` is the index into the `content` vector and should be the result of calling
/// `decode_cursor`, which returns a valid index.
fn element_at(&self, index: usize) -> Option<&[u8]> {
let mut slice = &self.content[index..]; // Start of element.
let element_len = compact_size::decode_unchecked(&mut slice);
// Compact size should always fit into a u32 because of `MAX_SIZE` in Core.
// ref: https://github.com/rust-bitcoin/rust-bitcoin/issues/3264
let end = element_len as usize;
Some(&slice[..end])
}
/// Returns the last element in the witness, if any.
pub fn last(&self) -> Option<&[u8]> {
if self.witness_elements == 0 {
None
} else {
self.nth(self.witness_elements - 1)
}
}
/// Returns the second-to-last element in the witness, if any.
pub fn second_to_last(&self) -> Option<&[u8]> {
if self.witness_elements <= 1 {
None
} else {
self.nth(self.witness_elements - 2)
}
}
/// 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)
}
/// Get Tapscript following BIP341 rules regarding accounting for an annex.
///
/// This does not guarantee that this represents a P2TR [`Witness`]. It
@ -412,7 +148,7 @@ impl Witness {
/// the first byte of the last element being equal to 0x50.
///
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
pub fn tapscript(&self) -> Option<&Script> {
fn tapscript(&self) -> Option<&Script> {
self.last().and_then(|last| {
// From BIP341:
// If there are at least two witness elements, and the first byte of
@ -435,7 +171,7 @@ impl Witness {
/// byte of the last element being equal to 0x50.
///
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
pub fn taproot_control_block(&self) -> Option<&[u8]> {
fn taproot_control_block(&self) -> Option<&[u8]> {
self.last().and_then(|last| {
// From BIP341:
// If there are at least two witness elements, and the first byte of
@ -456,7 +192,7 @@ impl Witness {
/// This does not guarantee that this represents a P2TR [`Witness`].
///
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
pub fn taproot_annex(&self) -> Option<&[u8]> {
fn taproot_annex(&self) -> Option<&[u8]> {
self.last().and_then(|last| {
// From BIP341:
// If there are at least two witness elements, and the first byte of
@ -475,154 +211,19 @@ impl Witness {
/// This does not guarantee that this represents a P2WS [`Witness`].
///
/// See [`Script::is_p2wsh`] to check whether this is actually a P2WSH witness.
pub fn witness_script(&self) -> Option<&Script> { self.last().map(Script::from_bytes) }
}
fn witness_script(&self) -> Option<&Script> { self.last().map(Script::from_bytes) }
impl Index<usize> for Witness {
type Output = [u8];
fn index(&self, index: usize) -> &Self::Output { self.nth(index).expect("out of bounds") }
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
let index = decode_cursor(self.inner, self.indices_start, self.current_index)?;
let mut slice = &self.inner[index..]; // Start of element.
let element_len = compact_size::decode_unchecked(&mut slice);
// Compact size should always fit into a u32 because of `MAX_SIZE` in Core.
// ref: https://github.com/rust-bitcoin/rust-bitcoin/issues/3264
let end = element_len as usize;
self.current_index += 1;
Some(&slice[..end])
}
fn size_hint(&self) -> (usize, Option<usize>) {
let total_count = (self.inner.len() - self.indices_start) / 4;
let remaining = total_count - self.current_index;
(remaining, Some(remaining))
}
}
impl<'a> ExactSizeIterator for Iter<'a> {}
impl<'a> IntoIterator for &'a Witness {
type IntoIter = Iter<'a>;
type Item = &'a [u8];
fn into_iter(self) -> Self::IntoIter { self.iter() }
}
// 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,
{
use serde::ser::SerializeSeq;
let human_readable = serializer.is_human_readable();
let mut seq = serializer.serialize_seq(Some(self.witness_elements))?;
// Note that the `Iter` strips the varints out when iterating.
for elem in self.iter() {
if human_readable {
seq.serialize_element(&crate::serde_utils::SerializeBytesAsHex(elem))?;
} else {
seq.serialize_element(&elem)?;
}
}
seq.end()
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Witness {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use crate::prelude::String;
struct Visitor; // Human-readable visitor.
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = Witness;
fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "a sequence of hex arrays")
}
fn visit_seq<A: serde::de::SeqAccess<'de>>(
self,
mut a: A,
) -> Result<Self::Value, A::Error> {
use hex::FromHex;
use hex::HexToBytesError::*;
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>()? {
let vec = Vec::<u8>::from_hex(&elem).map_err(|e| match e {
InvalidChar(ref e) => match core::char::from_u32(e.invalid_char().into()) {
Some(c) => de::Error::invalid_value(
Unexpected::Char(c),
&"a valid hex character",
),
None => de::Error::invalid_value(
Unexpected::Unsigned(e.invalid_char().into()),
&"a valid hex character",
),
},
OddLengthString(ref e) =>
de::Error::invalid_length(e.length(), &"an even length string"),
})?;
ret.push(vec);
}
Ok(Witness::from_slice(&ret))
}
}
if deserializer.is_human_readable() {
deserializer.deserialize_seq(Visitor)
} else {
let vec: Vec<Vec<u8>> = serde::Deserialize::deserialize(deserializer)?;
Ok(Witness::from_slice(&vec))
}
}
}
impl From<Vec<Vec<u8>>> for Witness {
fn from(vec: Vec<Vec<u8>>) -> Self { Witness::from_slice(&vec) }
}
impl From<&[&[u8]]> for Witness {
fn from(slice: &[&[u8]]) -> Self { Witness::from_slice(slice) }
}
impl From<&[Vec<u8>]> for Witness {
fn from(slice: &[Vec<u8>]) -> Self { Witness::from_slice(slice) }
}
impl From<Vec<&[u8]>> for Witness {
fn from(vec: Vec<&[u8]>) -> Self { Witness::from_slice(&vec) }
}
impl Default for Witness {
fn default() -> Self { Self::new() }
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Witness {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let arbitrary_bytes = Vec::<Vec<u8>>::arbitrary(u)?;
Ok(Witness::from_slice(&arbitrary_bytes))
}
/// Correctness Requirements: value must always fit within u32
// This is duplicated in `primitives::witness`, if you change it please do so over there also.
#[inline]
fn encode_cursor(bytes: &mut [u8], start_of_indices: usize, index: usize, value: usize) {
let start = start_of_indices + index * 4;
let end = start + 4;
bytes[start..end]
.copy_from_slice(&u32::to_ne_bytes(value.try_into().expect("larger than u32")));
}
#[cfg(test)]
@ -635,120 +236,6 @@ mod test {
use crate::sighash::EcdsaSighashType;
use crate::Transaction;
// Appends all the indices onto the end of a list of elements.
fn append_u32_vec(elements: &[u8], indices: &[u32]) -> Vec<u8> {
let mut v = elements.to_vec();
for &num in indices {
v.extend_from_slice(&num.to_ne_bytes());
}
v
}
// A witness with a single element that is empty (zero length).
fn single_empty_element() -> Witness {
// The first is 0 serialized as a compact size integer.
// The last four bytes represent start at index 0.
let content = [0_u8; 5];
Witness { witness_elements: 1, content: content.to_vec(), indices_start: 1 }
}
#[test]
fn witness_debug_can_display_empty_element() {
let witness = single_empty_element();
println!("{:?}", witness);
}
#[test]
fn witness_single_empty_element() {
let mut got = Witness::new();
got.push(&[]);
let want = single_empty_element();
assert_eq!(got, want)
}
#[test]
fn push() {
// Sanity check default.
let mut witness = Witness::default();
assert_eq!(witness.last(), None);
assert_eq!(witness.second_to_last(), None);
assert_eq!(witness.nth(0), None);
assert_eq!(witness.nth(1), None);
assert_eq!(witness.nth(2), None);
assert_eq!(witness.nth(3), None);
// Push a single byte element onto the witness stack.
let push = [0_u8];
witness.push(&push);
let elements = [1u8, 0];
let expected = Witness {
witness_elements: 1,
content: append_u32_vec(&elements, &[0]), // Start at index 0.
indices_start: elements.len(),
};
assert_eq!(witness, expected);
let element_0 = push.as_slice();
assert_eq!(element_0, &witness[0]);
assert_eq!(witness.second_to_last(), None);
assert_eq!(witness.last(), Some(element_0));
assert_eq!(witness.nth(0), Some(element_0));
assert_eq!(witness.nth(1), None);
assert_eq!(witness.nth(2), None);
assert_eq!(witness.nth(3), None);
// Now push 2 byte element onto the witness stack.
let push = [2u8, 3u8];
witness.push(&push);
let elements = [1u8, 0, 2, 2, 3];
let expected = Witness {
witness_elements: 2,
content: append_u32_vec(&elements, &[0, 2]),
indices_start: elements.len(),
};
assert_eq!(witness, expected);
let element_1 = push.as_slice();
assert_eq!(element_1, &witness[1]);
assert_eq!(witness.nth(0), Some(element_0));
assert_eq!(witness.nth(1), Some(element_1));
assert_eq!(witness.nth(2), None);
assert_eq!(witness.nth(3), None);
assert_eq!(witness.second_to_last(), Some(element_0));
assert_eq!(witness.last(), Some(element_1));
// Now push another 2 byte element onto the witness stack.
let push = [4u8, 5u8];
witness.push(&push);
let elements = [1u8, 0, 2, 2, 3, 2, 4, 5];
let expected = Witness {
witness_elements: 3,
content: append_u32_vec(&elements, &[0, 2, 5]),
indices_start: elements.len(),
};
assert_eq!(witness, expected);
let element_2 = push.as_slice();
assert_eq!(element_2, &witness[2]);
assert_eq!(witness.nth(0), Some(element_0));
assert_eq!(witness.nth(1), Some(element_1));
assert_eq!(witness.nth(2), Some(element_2));
assert_eq!(witness.nth(3), None);
assert_eq!(witness.second_to_last(), Some(element_1));
assert_eq!(witness.last(), Some(element_2));
}
#[test]
fn exact_sized_iterator() {
let mut witness = Witness::default();
@ -917,62 +404,13 @@ mod test {
let bytes = hex!("24000000ffffffffffffffffffffffff");
assert!(deserialize::<Witness>(&bytes).is_err()); // OversizedVectorAllocation
}
#[test]
#[cfg(feature = "serde")]
fn serde_bincode_backward_compatibility() {
let old_witness_format = vec![vec![0u8], vec![2]];
let new_witness_format = Witness::from_slice(&old_witness_format);
let old = bincode::serialize(&old_witness_format).unwrap();
let new = bincode::serialize(&new_witness_format).unwrap();
assert_eq!(old, new);
}
#[cfg(feature = "serde")]
fn arbitrary_witness() -> Witness {
let mut witness = Witness::default();
witness.push(&[0_u8]);
witness.push(&[1_u8; 32]);
witness.push(&[2_u8; 72]);
witness
}
#[test]
#[cfg(feature = "serde")]
fn serde_bincode_roundtrips() {
let original = arbitrary_witness();
let ser = bincode::serialize(&original).unwrap();
let rinsed: Witness = bincode::deserialize(&ser).unwrap();
assert_eq!(rinsed, original);
}
#[test]
#[cfg(feature = "serde")]
fn serde_human_roundtrips() {
let original = arbitrary_witness();
let ser = serde_json::to_string(&original).unwrap();
let rinsed: Witness = serde_json::from_str(&ser).unwrap();
assert_eq!(rinsed, original);
}
#[test]
#[cfg(feature = "serde")]
fn serde_human() {
let witness = Witness::from_slice(&[vec![0u8, 123, 75], vec![2u8, 6, 3, 7, 8]]);
let json = serde_json::to_string(&witness).unwrap();
assert_eq!(json, r#"["007b4b","0206030708"]"#);
}
}
#[cfg(bench)]
mod benches {
use test::{black_box, Bencher};
use super::Witness;
use super::{Witness, WitnessExt};
#[bench]
pub fn bench_big_witness_to_vec(bh: &mut Bencher) {

View File

@ -15,12 +15,13 @@ exclude = ["tests", "contrib"]
[features]
default = []
std = ["alloc"]
alloc = []
std = ["alloc", "hex/std"]
alloc = ["hex/alloc"]
test-serde = ["serde", "serde_json", "bincode"]
[dependencies]
hex = { package = "hex-conservative", version = "0.2.0", default-features = false }
serde = { version = "1.0.103", default-features = false, optional = true }
# Don't enable these directly, use `test-serde` feature instead.

View File

@ -309,3 +309,17 @@ macro_rules! serde_round_trip (
assert_eq!($var, decoded);
})
);
/// Serializes a byte slice using the `hex` crate.
pub struct SerializeBytesAsHex<'a>(pub &'a [u8]);
impl<'a> serde::Serialize for SerializeBytesAsHex<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use hex::DisplayHex;
serializer.collect_str(&format_args!("{:x}", self.0.as_hex()))
}
}

View File

@ -18,7 +18,7 @@ exclude = ["tests", "contrib"]
default = ["std"]
std = ["alloc", "hashes/std", "hex/std", "internals/std", "io/std", "units/std"]
alloc = ["hashes/alloc", "hex/alloc", "internals/alloc", "io/alloc", "units/alloc"]
serde = ["dep:serde", "hashes/serde", "hex/serde", "internals/serde", "units/serde", "alloc"]
serde = ["dep:serde", "hashes/serde", "internals/serde", "units/serde", "alloc"]
arbitrary = ["dep:arbitrary", "units/arbitrary"]
[dependencies]
@ -33,6 +33,8 @@ ordered = { version = "0.2.0", optional = true }
serde = { version = "1.0.103", default-features = false, features = ["derive", "alloc"], optional = true }
[dev-dependencies]
serde_json = "1.0.0"
bincode = "1.3.1"
[target.'cfg(mutate)'.dev-dependencies]
mutagen = { git = "https://github.com/llogiq/mutagen" }

View File

@ -3,6 +3,11 @@
This crate provides primitive data types that are used throughout the
[`rust-bitcoin`](https://github.com/rust-bitcoin) ecosystem.
## Semver compliance
Functions marked as unstable (e.g. `foo__unstable`) are not guaranteed to uphold semver compliance.
They are primarily provided to support `rust-bitcoin`.
## Minimum Supported Rust Version (MSRV)
This library should always compile with any combination of features on **Rust 1.63.0**.

View File

@ -40,13 +40,18 @@ pub mod pow;
pub mod script;
pub mod sequence;
pub mod transaction;
#[cfg(feature = "alloc")]
pub mod witness;
#[doc(inline)]
pub use units::*;
#[doc(inline)]
#[cfg(feature = "alloc")]
pub use self::locktime::{absolute, relative};
pub use self::{
locktime::{absolute, relative},
witness::Witness,
};
#[doc(inline)]
pub use self::{
block::{BlockHash, WitnessCommitment},

636
primitives/src/witness.rs Normal file
View File

@ -0,0 +1,636 @@
// SPDX-License-Identifier: CC0-1.0
//! A witness.
//!
//! This module contains the [`Witness`] struct and related methods to operate on it
use core::fmt;
use core::ops::Index;
#[cfg(feature = "arbitrary")]
use arbitrary::{Arbitrary, Unstructured};
use internals::compact_size;
use crate::prelude::Vec;
/// The Witness is the data used to unlock bitcoin since the [segwit upgrade].
///
/// 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_bytes`].
///
/// For serialization and deserialization performance it is stored internally as a single `Vec`,
/// saving some allocations.
///
/// [segwit upgrade]: <https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki>
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Witness {
/// Contains the witness `Vec<Vec<u8>>` serialization.
///
/// Does not include the initial varint indicating the number of elements. Each element however,
/// does include a varint indicating the element length. The number of elements is stored in
/// `witness_elements`.
///
/// Concatenated onto the end of `content` is the index area. This is a `4 * witness_elements`
/// bytes area which stores the index of the start of each witness item.
content: Vec<u8>,
/// The number of elements in the witness.
///
/// Stored separately (instead of as a compact size encoding in the initial part of content) so
/// that methods like [`Witness::push`] don't have to shift the entire array.
witness_elements: usize,
/// This is the valid index pointing to the beginning of the index area.
///
/// Said another way, this is the total length of all witness elements serialized (without the
/// element count but with their sizes serialized as compact size).
indices_start: usize,
}
impl Witness {
/// Creates a new empty [`Witness`].
#[inline]
pub const fn new() -> Self {
Witness { content: Vec::new(), witness_elements: 0, indices_start: 0 }
}
/// Creates a new [`Witness`] from inner parts.
///
/// This function leaks implementation details of the `Witness`, as such it is unstable and
/// should not be relied upon (it is primarily provided for use in `rust-bitcoin`).
///
/// UNSTABLE: This function may change, break, or disappear in any release.
#[inline]
#[doc(hidden)]
#[allow(non_snake_case)] // Because of `__unstable`.
pub fn from_parts__unstable(content: Vec<u8>, witness_elements: usize, indices_start: usize) -> Self {
Witness { content, witness_elements, indices_start }
}
/// 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
.iter()
.map(|elem| elem.as_ref().len() + compact_size::encoded_size(elem.as_ref().len()))
.sum();
let mut content = alloc::vec![0u8; content_size + index_size];
let mut cursor = 0usize;
for (i, elem) in slice.iter().enumerate() {
encode_cursor(&mut content, content_size, i, cursor);
let encoded = compact_size::encode(elem.as_ref().len());
let encoded_size = encoded.as_slice().len();
content[cursor..cursor + encoded_size].copy_from_slice(encoded.as_slice());
cursor += encoded_size;
content[cursor..cursor + elem.as_ref().len()].copy_from_slice(elem.as_ref());
cursor += elem.as_ref().len();
}
Witness { witness_elements, content, indices_start: content_size }
}
/// Convenience method to create an array of byte-arrays from this witness.
pub fn to_bytes(&self) -> Vec<Vec<u8>> { self.iter().map(|s| s.to_vec()).collect() }
/// Returns `true` if the witness contains no element.
pub fn is_empty(&self) -> bool { self.witness_elements == 0 }
/// Returns a struct implementing [`Iterator`].
pub fn iter(&self) -> Iter {
Iter { inner: self.content.as_slice(), indices_start: self.indices_start, current_index: 0 }
}
/// Returns the number of elements this witness holds.
pub fn len(&self) -> usize { self.witness_elements }
/// Returns the number of bytes this witness contributes to a transactions total size.
pub fn size(&self) -> usize {
let mut size: usize = 0;
size += compact_size::encoded_size(self.witness_elements);
size += self
.iter()
.map(|witness_element| {
let len = witness_element.len();
compact_size::encoded_size(len) + len
})
.sum::<usize>();
size
}
/// Clear the witness.
pub fn clear(&mut self) {
self.content.clear();
self.witness_elements = 0;
self.indices_start = 0;
}
/// Push a new element on the witness, requires an allocation.
pub fn push<T: AsRef<[u8]>>(&mut self, new_element: T) {
self.push_slice(new_element.as_ref());
}
/// Push a new element slice onto the witness stack.
fn push_slice(&mut self, new_element: &[u8]) {
self.witness_elements += 1;
let previous_content_end = self.indices_start;
let encoded = compact_size::encode(new_element.len());
let encoded_size = encoded.as_slice().len();
let current_content_len = self.content.len();
let new_item_total_len = encoded_size + new_element.len();
self.content.resize(current_content_len + new_item_total_len + 4, 0);
self.content[previous_content_end..].rotate_right(new_item_total_len);
self.indices_start += new_item_total_len;
encode_cursor(
&mut self.content,
self.indices_start,
self.witness_elements - 1,
previous_content_end,
);
let end_compact_size = previous_content_end + encoded_size;
self.content[previous_content_end..end_compact_size].copy_from_slice(encoded.as_slice());
self.content[end_compact_size..end_compact_size + new_element.len()]
.copy_from_slice(new_element);
}
/// Note `index` is the index into the `content` vector and should be the result of calling
/// `decode_cursor`, which returns a valid index.
fn element_at(&self, index: usize) -> Option<&[u8]> {
let mut slice = &self.content[index..]; // Start of element.
let element_len = compact_size::decode_unchecked(&mut slice);
// Compact size should always fit into a u32 because of `MAX_SIZE` in Core.
// ref: https://github.com/rust-bitcoin/rust-bitcoin/issues/3264
let end = element_len as usize;
Some(&slice[..end])
}
/// Returns the last element in the witness, if any.
pub fn last(&self) -> Option<&[u8]> {
if self.witness_elements == 0 {
None
} else {
self.nth(self.witness_elements - 1)
}
}
/// Returns the second-to-last element in the witness, if any.
pub fn second_to_last(&self) -> Option<&[u8]> {
if self.witness_elements <= 1 {
None
} else {
self.nth(self.witness_elements - 2)
}
}
/// 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)
}
}
/// Correctness Requirements: value must always fit within u32
// This is duplicated in `bitcoin::blockdata::witness`, if you change it please do so over there also.
#[inline]
fn encode_cursor(bytes: &mut [u8], start_of_indices: usize, index: usize, value: usize) {
let start = start_of_indices + index * 4;
let end = start + 4;
bytes[start..end]
.copy_from_slice(&u32::to_ne_bytes(value.try_into().expect("larger than u32")));
}
// This is duplicated in `bitcoin::blockdata::witness`, if you change them do so over there also.
#[inline]
fn decode_cursor(bytes: &[u8], start_of_indices: usize, index: usize) -> Option<usize> {
let start = start_of_indices + index * 4;
let end = start + 4;
if end > bytes.len() {
None
} else {
Some(u32::from_ne_bytes(bytes[start..end].try_into().expect("is u32 size")) as usize)
}
}
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: { ")?;
write!(f, "indices: {}, ", w.witness_elements)?;
write!(f, "indices_start: {}, ", w.indices_start)?;
f.write_str("witnesses: [")?;
let instructions = w.iter();
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, "[]")?;
}
}
f.write_str(comma_or_close(i, last_instruction))?;
}
}
None => {
// Witnesses can be empty because the 0x00 var int is not stored in content.
write!(f, "]")?;
}
}
f.write_str(" }")
}
fn fmt_debug_pretty(w: &Witness, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
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 {
f.write_str(", ")?;
}
write!(f, "{:#04x}", byte)?;
}
f.write_str("],\n")?;
}
writeln!(f, " ],")?;
writeln!(f, "}}")
}
/// An iterator returning individual witness elements.
pub struct Iter<'a> {
inner: &'a [u8],
indices_start: usize,
current_index: usize,
}
impl Index<usize> for Witness {
type Output = [u8];
fn index(&self, index: usize) -> &Self::Output { self.nth(index).expect("out of bounds") }
}
impl<'a> Iterator for Iter<'a> {
type Item = &'a [u8];
fn next(&mut self) -> Option<Self::Item> {
let index = decode_cursor(self.inner, self.indices_start, self.current_index)?;
let mut slice = &self.inner[index..]; // Start of element.
let element_len = compact_size::decode_unchecked(&mut slice);
// Compact size should always fit into a u32 because of `MAX_SIZE` in Core.
// ref: https://github.com/rust-bitcoin/rust-bitcoin/issues/3264
let end = element_len as usize;
self.current_index += 1;
Some(&slice[..end])
}
fn size_hint(&self) -> (usize, Option<usize>) {
let total_count = (self.inner.len() - self.indices_start) / 4;
let remaining = total_count - self.current_index;
(remaining, Some(remaining))
}
}
impl<'a> ExactSizeIterator for Iter<'a> {}
impl<'a> IntoIterator for &'a Witness {
type IntoIter = Iter<'a>;
type Item = &'a [u8];
fn into_iter(self) -> Self::IntoIter { self.iter() }
}
// 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,
{
use serde::ser::SerializeSeq;
let human_readable = serializer.is_human_readable();
let mut seq = serializer.serialize_seq(Some(self.witness_elements))?;
// Note that the `Iter` strips the varints out when iterating.
for elem in self.iter() {
if human_readable {
seq.serialize_element(&internals::serde::SerializeBytesAsHex(elem))?;
} else {
seq.serialize_element(&elem)?;
}
}
seq.end()
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Witness {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use crate::prelude::String;
struct Visitor; // Human-readable visitor.
impl<'de> serde::de::Visitor<'de> for Visitor {
type Value = Witness;
fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "a sequence of hex arrays")
}
fn visit_seq<A: serde::de::SeqAccess<'de>>(
self,
mut a: A,
) -> Result<Self::Value, A::Error> {
use hex::FromHex;
use hex::HexToBytesError::*;
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>()? {
let vec = Vec::<u8>::from_hex(&elem).map_err(|e| match e {
InvalidChar(ref e) => match core::char::from_u32(e.invalid_char().into()) {
Some(c) => de::Error::invalid_value(
Unexpected::Char(c),
&"a valid hex character",
),
None => de::Error::invalid_value(
Unexpected::Unsigned(e.invalid_char().into()),
&"a valid hex character",
),
},
OddLengthString(ref e) =>
de::Error::invalid_length(e.length(), &"an even length string"),
})?;
ret.push(vec);
}
Ok(Witness::from_slice(&ret))
}
}
if deserializer.is_human_readable() {
deserializer.deserialize_seq(Visitor)
} else {
let vec: Vec<Vec<u8>> = serde::Deserialize::deserialize(deserializer)?;
Ok(Witness::from_slice(&vec))
}
}
}
impl From<Vec<Vec<u8>>> for Witness {
fn from(vec: Vec<Vec<u8>>) -> Self { Witness::from_slice(&vec) }
}
impl From<&[&[u8]]> for Witness {
fn from(slice: &[&[u8]]) -> Self { Witness::from_slice(slice) }
}
impl From<&[Vec<u8>]> for Witness {
fn from(slice: &[Vec<u8>]) -> Self { Witness::from_slice(slice) }
}
impl From<Vec<&[u8]>> for Witness {
fn from(vec: Vec<&[u8]>) -> Self { Witness::from_slice(&vec) }
}
impl Default for Witness {
fn default() -> Self { Self::new() }
}
#[cfg(feature = "arbitrary")]
impl<'a> Arbitrary<'a> for Witness {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let arbitrary_bytes = Vec::<Vec<u8>>::arbitrary(u)?;
Ok(Witness::from_slice(&arbitrary_bytes))
}
}
#[cfg(test)]
mod test {
use super::*;
// Appends all the indices onto the end of a list of elements.
fn append_u32_vec(elements: &[u8], indices: &[u32]) -> Vec<u8> {
let mut v = elements.to_vec();
for &num in indices {
v.extend_from_slice(&num.to_ne_bytes());
}
v
}
// A witness with a single element that is empty (zero length).
fn single_empty_element() -> Witness {
// The first is 0 serialized as a compact size integer.
// The last four bytes represent start at index 0.
let content = [0_u8; 5];
Witness {
witness_elements: 1,
content: content.to_vec(),
indices_start: 1,
}
}
#[test]
fn witness_debug_can_display_empty_element() {
let witness = single_empty_element();
println!("{:?}", witness);
}
#[test]
fn witness_single_empty_element() {
let mut got = Witness::new();
got.push(&[]);
let want = single_empty_element();
assert_eq!(got, want)
}
#[test]
fn push() {
// Sanity check default.
let mut witness = Witness::default();
assert_eq!(witness.last(), None);
assert_eq!(witness.second_to_last(), None);
assert_eq!(witness.nth(0), None);
assert_eq!(witness.nth(1), None);
assert_eq!(witness.nth(2), None);
assert_eq!(witness.nth(3), None);
// Push a single byte element onto the witness stack.
let push = [0_u8];
witness.push(&push);
let elements = [1u8, 0];
let expected = Witness {
witness_elements: 1,
content: append_u32_vec(&elements, &[0]), // Start at index 0.
indices_start: elements.len(),
};
assert_eq!(witness, expected);
let element_0 = push.as_slice();
assert_eq!(element_0, &witness[0]);
assert_eq!(witness.second_to_last(), None);
assert_eq!(witness.last(), Some(element_0));
assert_eq!(witness.nth(0), Some(element_0));
assert_eq!(witness.nth(1), None);
assert_eq!(witness.nth(2), None);
assert_eq!(witness.nth(3), None);
// Now push 2 byte element onto the witness stack.
let push = [2u8, 3u8];
witness.push(&push);
let elements = [1u8, 0, 2, 2, 3];
let expected = Witness {
witness_elements: 2,
content: append_u32_vec(&elements, &[0, 2]),
indices_start: elements.len(),
};
assert_eq!(witness, expected);
let element_1 = push.as_slice();
assert_eq!(element_1, &witness[1]);
assert_eq!(witness.nth(0), Some(element_0));
assert_eq!(witness.nth(1), Some(element_1));
assert_eq!(witness.nth(2), None);
assert_eq!(witness.nth(3), None);
assert_eq!(witness.second_to_last(), Some(element_0));
assert_eq!(witness.last(), Some(element_1));
// Now push another 2 byte element onto the witness stack.
let push = [4u8, 5u8];
witness.push(&push);
let elements = [1u8, 0, 2, 2, 3, 2, 4, 5];
let expected = Witness {
witness_elements: 3,
content: append_u32_vec(&elements, &[0, 2, 5]),
indices_start: elements.len(),
};
assert_eq!(witness, expected);
let element_2 = push.as_slice();
assert_eq!(element_2, &witness[2]);
assert_eq!(witness.nth(0), Some(element_0));
assert_eq!(witness.nth(1), Some(element_1));
assert_eq!(witness.nth(2), Some(element_2));
assert_eq!(witness.nth(3), None);
assert_eq!(witness.second_to_last(), Some(element_1));
assert_eq!(witness.last(), Some(element_2));
}
#[test]
fn exact_sized_iterator() {
let arbitrary_element = [1_u8, 2, 3];
let num_pushes = 5; // Somewhat arbitrary.
let mut witness = Witness::default();
for i in 0..num_pushes {
assert_eq!(witness.iter().len(), i);
witness.push(&arbitrary_element);
}
let mut iter = witness.iter();
for i in (0..=num_pushes).rev() {
assert_eq!(iter.len(), i);
iter.next();
}
}
#[test]
#[cfg(feature = "serde")]
fn serde_bincode_backward_compatibility() {
let old_witness_format = vec![vec![0u8], vec![2]];
let new_witness_format = Witness::from_slice(&old_witness_format);
let old = bincode::serialize(&old_witness_format).unwrap();
let new = bincode::serialize(&new_witness_format).unwrap();
assert_eq!(old, new);
}
#[cfg(feature = "serde")]
fn arbitrary_witness() -> Witness {
let mut witness = Witness::default();
witness.push(&[0_u8]);
witness.push(&[1_u8; 32]);
witness.push(&[2_u8; 72]);
witness
}
#[test]
#[cfg(feature = "serde")]
fn serde_bincode_roundtrips() {
let original = arbitrary_witness();
let ser = bincode::serialize(&original).unwrap();
let rinsed: Witness = bincode::deserialize(&ser).unwrap();
assert_eq!(rinsed, original);
}
#[test]
#[cfg(feature = "serde")]
fn serde_human_roundtrips() {
let original = arbitrary_witness();
let ser = serde_json::to_string(&original).unwrap();
let rinsed: Witness = serde_json::from_str(&ser).unwrap();
assert_eq!(rinsed, original);
}
#[test]
#[cfg(feature = "serde")]
fn serde_human() {
let witness = Witness::from_slice(&[vec![0u8, 123, 75], vec![2u8, 6, 3, 7, 8]]);
let json = serde_json::to_string(&witness).unwrap();
assert_eq!(json, r#"["007b4b","0206030708"]"#);
}
}