Move Witness to primitives
Move the `Witness` over to `primitives` leaving behind any method that takes or returns a `Script` or a signature. Includes addition of a feature gate to unit test.
This commit is contained in:
parent
6ce76cd7c8
commit
c1eccfde25
|
@ -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]]
|
||||
|
@ -197,7 +200,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e1aa273bf451e37ed35ced41c71a5e2a4e29064afb104158f2514bcd71c2c986"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -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]]
|
||||
|
@ -199,7 +202,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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> {
|
||||
|
@ -199,26 +84,6 @@ impl Decodable for Witness {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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);
|
||||
|
@ -242,401 +107,123 @@ 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 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 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(signature.serialize());
|
||||
witness.push(pubkey.serialize());
|
||||
witness
|
||||
}
|
||||
|
||||
/// Creates a witness required to do a key path spend of a P2TR output.
|
||||
pub 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();
|
||||
/// 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`.
|
||||
fn p2wpkh(signature: ecdsa::Signature, pubkey: secp256k1::PublicKey) -> Witness {
|
||||
let mut witness = Witness::new();
|
||||
witness.push(signature.serialize());
|
||||
witness.push(pubkey.serialize());
|
||||
witness
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
/// Creates a witness required to do a key path spend of a P2TR output.
|
||||
fn p2tr_key_spend(signature: &taproot::Signature) -> Witness {
|
||||
let mut witness = Witness::new();
|
||||
witness.push(signature.serialize());
|
||||
witness
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
/// Pushes, as a new element on the witness, an ECDSA signature.
|
||||
///
|
||||
/// Pushes the DER encoded signature + sighash_type, requires an allocation.
|
||||
fn push_ecdsa_signature(&mut self, signature: ecdsa::Signature) {
|
||||
self.push(signature.serialize())
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
/// 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 [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
|
||||
pub 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
|
||||
// the last element is 0x50, this last element is called annex a
|
||||
// and is removed from the witness stack.
|
||||
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
|
||||
self.nth(self.len() - 3).map(Script::from_bytes)
|
||||
} else if self.len() >= 2 {
|
||||
self.nth(self.len() - 2).map(Script::from_bytes)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the taproot control block following BIP341 rules.
|
||||
///
|
||||
/// This does not guarantee that this represents a P2TR [`Witness`]. It
|
||||
/// merely gets the last or second to last element depending on 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 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
|
||||
// the last element is 0x50, this last element is called annex a
|
||||
// and is removed from the witness stack.
|
||||
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
|
||||
self.nth(self.len() - 2)
|
||||
} else if self.len() >= 2 {
|
||||
Some(last)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the taproot annex following BIP341 rules.
|
||||
///
|
||||
/// 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]> {
|
||||
self.last().and_then(|last| {
|
||||
// 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.
|
||||
if self.len() >= 2 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
|
||||
Some(last)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the p2wsh witness script following BIP141 rules.
|
||||
///
|
||||
/// 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) }
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/// Get Tapscript following BIP341 rules regarding accounting for an annex.
|
||||
///
|
||||
/// 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 [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
|
||||
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
|
||||
// the last element is 0x50, this last element is called annex a
|
||||
// and is removed from the witness stack.
|
||||
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
|
||||
self.nth(self.len() - 3).map(Script::from_bytes)
|
||||
} else if self.len() >= 2 {
|
||||
self.nth(self.len() - 2).map(Script::from_bytes)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
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))
|
||||
/// Get the taproot control block following BIP341 rules.
|
||||
///
|
||||
/// This does not guarantee that this represents a P2TR [`Witness`]. It
|
||||
/// merely gets the last or second to last element depending on the first
|
||||
/// byte of the last element being equal to 0x50.
|
||||
///
|
||||
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
|
||||
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
|
||||
// the last element is 0x50, this last element is called annex a
|
||||
// and is removed from the witness stack.
|
||||
if self.len() >= 3 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
|
||||
self.nth(self.len() - 2)
|
||||
} else if self.len() >= 2 {
|
||||
Some(last)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the taproot annex following BIP341 rules.
|
||||
///
|
||||
/// This does not guarantee that this represents a P2TR [`Witness`].
|
||||
///
|
||||
/// See [`Script::is_p2tr`] to check whether this is actually a Taproot witness.
|
||||
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
|
||||
// the last element is 0x50, this last element is called annex a
|
||||
// and is removed from the witness stack.
|
||||
if self.len() >= 2 && last.first() == Some(&TAPROOT_ANNEX_PREFIX) {
|
||||
Some(last)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the p2wsh witness script following BIP141 rules.
|
||||
///
|
||||
/// This does not guarantee that this represents a P2WS [`Witness`].
|
||||
///
|
||||
/// See [`Script::is_p2wsh`] to check whether this is actually a P2WSH witness.
|
||||
fn witness_script(&self) -> Option<&Script> { self.last().map(Script::from_bytes) }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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)]
|
||||
|
@ -649,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();
|
||||
|
@ -931,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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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**.
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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"]"#);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue