Merge rust-bitcoin/rust-bitcoin#681: Add support for taproot psbt fields BIP 371
7d982fa9a2
Add all tests from BIP 371 (sanket1729)d22e0149ad
Taproot psbt impl BIP 371 (sanket1729)108fc3d4db
Impl encodable traits for TapLeafhash (sanket1729)c7478d8fd0
Derive serde for taproot stuctures (sanket1729) Pull request description: Built on top of #677 . Will rebase and mark ready for review after #677 is merged. ACKs for top commit: apoelstra: ACK7d982fa9a2
dr-orlovsky: re-tACK7d982fa9a2
basing on `git range-diff`. The original PR before last re-base was tested commit-by-commit. Tree-SHA512: feb30e4b38d13110a9c0fabf6466d8f0fb7df09a82f4e01d70b8371b34ab0187004a6c63f9796c6585ee30841e8ee765ae9becae139d2e1e3d839553d64c3d1e
This commit is contained in:
commit
670e808c17
|
@ -39,6 +39,7 @@ use io::{self, Cursor, Read};
|
||||||
|
|
||||||
use util::endian;
|
use util::endian;
|
||||||
use util::psbt;
|
use util::psbt;
|
||||||
|
use util::taproot::TapLeafHash;
|
||||||
use hashes::hex::ToHex;
|
use hashes::hex::ToHex;
|
||||||
|
|
||||||
use blockdata::transaction::{TxOut, Transaction, TxIn};
|
use blockdata::transaction::{TxOut, Transaction, TxIn};
|
||||||
|
@ -594,6 +595,7 @@ impl_vec!(TxOut);
|
||||||
impl_vec!(TxIn);
|
impl_vec!(TxIn);
|
||||||
impl_vec!(Vec<u8>);
|
impl_vec!(Vec<u8>);
|
||||||
impl_vec!(u64);
|
impl_vec!(u64);
|
||||||
|
impl_vec!(TapLeafHash);
|
||||||
|
|
||||||
#[cfg(feature = "std")] impl_vec!(Inventory);
|
#[cfg(feature = "std")] impl_vec!(Inventory);
|
||||||
#[cfg(feature = "std")] impl_vec!((u32, Address));
|
#[cfg(feature = "std")] impl_vec!((u32, Address));
|
||||||
|
@ -767,6 +769,18 @@ impl Decodable for sha256::Hash {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Encodable for TapLeafHash {
|
||||||
|
fn consensus_encode<S: io::Write>(&self, s: S) -> Result<usize, io::Error> {
|
||||||
|
self.into_inner().consensus_encode(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for TapLeafHash {
|
||||||
|
fn consensus_decode<D: io::Read>(d: D) -> Result<Self, Error> {
|
||||||
|
Ok(Self::from_inner(<<Self as Hash>::Inner>::consensus_decode(d)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tests
|
// Tests
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
|
@ -28,6 +28,9 @@ use util::psbt::raw;
|
||||||
use util::psbt::serialize::Deserialize;
|
use util::psbt::serialize::Deserialize;
|
||||||
use util::psbt::{Error, error};
|
use util::psbt::{Error, error};
|
||||||
|
|
||||||
|
use schnorr;
|
||||||
|
use util::taproot::{ControlBlock, LeafVersion, TapLeafHash, TapBranchHash};
|
||||||
|
|
||||||
/// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00
|
/// Type: Non-Witness UTXO PSBT_IN_NON_WITNESS_UTXO = 0x00
|
||||||
const PSBT_IN_NON_WITNESS_UTXO: u8 = 0x00;
|
const PSBT_IN_NON_WITNESS_UTXO: u8 = 0x00;
|
||||||
/// Type: Witness UTXO PSBT_IN_WITNESS_UTXO = 0x01
|
/// Type: Witness UTXO PSBT_IN_WITNESS_UTXO = 0x01
|
||||||
|
@ -54,6 +57,18 @@ const PSBT_IN_SHA256: u8 = 0x0b;
|
||||||
const PSBT_IN_HASH160: u8 = 0x0c;
|
const PSBT_IN_HASH160: u8 = 0x0c;
|
||||||
/// Type: HASH256 preimage PSBT_IN_HASH256 = 0x0d
|
/// Type: HASH256 preimage PSBT_IN_HASH256 = 0x0d
|
||||||
const PSBT_IN_HASH256: u8 = 0x0d;
|
const PSBT_IN_HASH256: u8 = 0x0d;
|
||||||
|
/// Type: Schnorr Signature in Key Spend PSBT_IN_TAP_KEY_SIG = 0x13
|
||||||
|
const PSBT_IN_TAP_KEY_SIG: u8 = 0x13;
|
||||||
|
/// Type: Schnorr Signature in Script Spend PSBT_IN_TAP_SCRIPT_SIG = 0x14
|
||||||
|
const PSBT_IN_TAP_SCRIPT_SIG: u8 = 0x14;
|
||||||
|
/// Type: Taproot Leaf Script PSBT_IN_TAP_LEAF_SCRIPT = 0x14
|
||||||
|
const PSBT_IN_TAP_LEAF_SCRIPT: u8 = 0x15;
|
||||||
|
/// Type: Taproot Key BIP 32 Derivation Path PSBT_IN_TAP_BIP32_DERIVATION = 0x16
|
||||||
|
const PSBT_IN_TAP_BIP32_DERIVATION : u8 = 0x16;
|
||||||
|
/// Type: Taproot Internal Key PSBT_IN_TAP_INTERNAL_KEY = 0x17
|
||||||
|
const PSBT_IN_TAP_INTERNAL_KEY : u8 = 0x17;
|
||||||
|
/// Type: Taproot Merkle Root PSBT_IN_TAP_MERKLE_ROOT = 0x18
|
||||||
|
const PSBT_IN_TAP_MERKLE_ROOT : u8 = 0x18;
|
||||||
/// Type: Proprietary Use Type PSBT_IN_PROPRIETARY = 0xFC
|
/// Type: Proprietary Use Type PSBT_IN_PROPRIETARY = 0xFC
|
||||||
const PSBT_IN_PROPRIETARY: u8 = 0xFC;
|
const PSBT_IN_PROPRIETARY: u8 = 0xFC;
|
||||||
|
|
||||||
|
@ -104,6 +119,21 @@ pub struct Input {
|
||||||
/// HAS256 hash to preimage map
|
/// HAS256 hash to preimage map
|
||||||
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_byte_values"))]
|
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_byte_values"))]
|
||||||
pub hash256_preimages: BTreeMap<sha256d::Hash, Vec<u8>>,
|
pub hash256_preimages: BTreeMap<sha256d::Hash, Vec<u8>>,
|
||||||
|
/// Serialized schnorr signature with sighash type for key spend
|
||||||
|
pub tap_key_sig: Option<schnorr::SchnorrSig>,
|
||||||
|
/// Map of <xonlypubkey>|<leafhash> with signature
|
||||||
|
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq"))]
|
||||||
|
pub tap_script_sigs: BTreeMap<(schnorr::PublicKey, TapLeafHash), schnorr::SchnorrSig>,
|
||||||
|
/// Map of Control blocks to Script version pair
|
||||||
|
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq"))]
|
||||||
|
pub tap_scripts: BTreeMap<ControlBlock, (Script, LeafVersion)>,
|
||||||
|
/// Map of tap root x only keys to origin info and leaf hashes contained in it
|
||||||
|
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq"))]
|
||||||
|
pub tap_key_origins: BTreeMap<schnorr::PublicKey, (Vec<TapLeafHash>, KeySource)>,
|
||||||
|
/// Taproot Internal key
|
||||||
|
pub tap_internal_key : Option<schnorr::PublicKey>,
|
||||||
|
/// Taproot Merkle root
|
||||||
|
pub tap_merkle_root : Option<TapBranchHash>,
|
||||||
/// Proprietary key-value pairs for this input.
|
/// Proprietary key-value pairs for this input.
|
||||||
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq_byte_values"))]
|
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq_byte_values"))]
|
||||||
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
|
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
|
||||||
|
@ -177,6 +207,36 @@ impl Map for Input {
|
||||||
PSBT_IN_HASH256 => {
|
PSBT_IN_HASH256 => {
|
||||||
psbt_insert_hash_pair(&mut self.hash256_preimages, raw_key, raw_value, error::PsbtHash::Hash256)?;
|
psbt_insert_hash_pair(&mut self.hash256_preimages, raw_key, raw_value, error::PsbtHash::Hash256)?;
|
||||||
}
|
}
|
||||||
|
PSBT_IN_TAP_KEY_SIG => {
|
||||||
|
impl_psbt_insert_pair! {
|
||||||
|
self.tap_key_sig <= <raw_key: _>|<raw_value: schnorr::SchnorrSig>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PSBT_IN_TAP_SCRIPT_SIG => {
|
||||||
|
impl_psbt_insert_pair! {
|
||||||
|
self.tap_script_sigs <= <raw_key: (schnorr::PublicKey, TapLeafHash)>|<raw_value: schnorr::SchnorrSig>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PSBT_IN_TAP_LEAF_SCRIPT=> {
|
||||||
|
impl_psbt_insert_pair! {
|
||||||
|
self.tap_scripts <= <raw_key: ControlBlock>|< raw_value: (Script, LeafVersion)>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PSBT_IN_TAP_BIP32_DERIVATION => {
|
||||||
|
impl_psbt_insert_pair! {
|
||||||
|
self.tap_key_origins <= <raw_key: schnorr::PublicKey>|< raw_value: (Vec<TapLeafHash>, KeySource)>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PSBT_IN_TAP_INTERNAL_KEY => {
|
||||||
|
impl_psbt_insert_pair! {
|
||||||
|
self.tap_internal_key <= <raw_key: _>|< raw_value: schnorr::PublicKey>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PSBT_IN_TAP_MERKLE_ROOT => {
|
||||||
|
impl_psbt_insert_pair! {
|
||||||
|
self.tap_merkle_root <= <raw_key: _>|< raw_value: TapBranchHash>
|
||||||
|
}
|
||||||
|
}
|
||||||
PSBT_IN_PROPRIETARY => match self.proprietary.entry(raw::ProprietaryKey::from_key(raw_key.clone())?) {
|
PSBT_IN_PROPRIETARY => match self.proprietary.entry(raw::ProprietaryKey::from_key(raw_key.clone())?) {
|
||||||
btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
||||||
btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key).into()),
|
btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key).into()),
|
||||||
|
@ -249,6 +309,30 @@ impl Map for Input {
|
||||||
rv.push(self.hash256_preimages as <PSBT_IN_HASH256, sha256d::Hash>|<Vec<u8>>)
|
rv.push(self.hash256_preimages as <PSBT_IN_HASH256, sha256d::Hash>|<Vec<u8>>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_psbt_get_pair! {
|
||||||
|
rv.push(self.tap_key_sig as <PSBT_IN_TAP_KEY_SIG, _>|<Vec<u8>>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_psbt_get_pair! {
|
||||||
|
rv.push(self.tap_script_sigs as <PSBT_IN_TAP_SCRIPT_SIG, (schnorr::PublicKey, TapLeafHash)>|<Vec<u8>>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_psbt_get_pair! {
|
||||||
|
rv.push(self.tap_scripts as <PSBT_IN_TAP_LEAF_SCRIPT, ControlBlock>|<(Script, LeafVersion)>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_psbt_get_pair! {
|
||||||
|
rv.push(self.tap_key_origins as <PSBT_IN_TAP_BIP32_DERIVATION,
|
||||||
|
schnorr::PublicKey>|<(Vec<TapLeafHash>, KeySource)>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_psbt_get_pair! {
|
||||||
|
rv.push(self.tap_internal_key as <PSBT_IN_TAP_INTERNAL_KEY, _>|<schnorr::PublicKey>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_psbt_get_pair! {
|
||||||
|
rv.push(self.tap_merkle_root as <PSBT_IN_TAP_MERKLE_ROOT, _>|<TapBranchHash>)
|
||||||
|
}
|
||||||
for (key, value) in self.proprietary.iter() {
|
for (key, value) in self.proprietary.iter() {
|
||||||
rv.push(raw::Pair {
|
rv.push(raw::Pair {
|
||||||
key: key.to_key(),
|
key: key.to_key(),
|
||||||
|
@ -280,6 +364,9 @@ impl Map for Input {
|
||||||
self.sha256_preimages.extend(other.sha256_preimages);
|
self.sha256_preimages.extend(other.sha256_preimages);
|
||||||
self.hash160_preimages.extend(other.hash160_preimages);
|
self.hash160_preimages.extend(other.hash160_preimages);
|
||||||
self.hash256_preimages.extend(other.hash256_preimages);
|
self.hash256_preimages.extend(other.hash256_preimages);
|
||||||
|
self.tap_script_sigs.extend(other.tap_script_sigs);
|
||||||
|
self.tap_scripts.extend(other.tap_scripts);
|
||||||
|
self.tap_key_origins.extend(other.tap_key_origins);
|
||||||
self.proprietary.extend(other.proprietary);
|
self.proprietary.extend(other.proprietary);
|
||||||
self.unknown.extend(other.unknown);
|
self.unknown.extend(other.unknown);
|
||||||
|
|
||||||
|
@ -287,6 +374,9 @@ impl Map for Input {
|
||||||
merge!(witness_script, self, other);
|
merge!(witness_script, self, other);
|
||||||
merge!(final_script_sig, self, other);
|
merge!(final_script_sig, self, other);
|
||||||
merge!(final_script_witness, self, other);
|
merge!(final_script_witness, self, other);
|
||||||
|
merge!(tap_key_sig, self, other);
|
||||||
|
merge!(tap_internal_key, self, other);
|
||||||
|
merge!(tap_merkle_root, self, other);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,3 +55,4 @@ mod output;
|
||||||
|
|
||||||
pub use self::input::Input;
|
pub use self::input::Input;
|
||||||
pub use self::output::Output;
|
pub use self::output::Output;
|
||||||
|
pub use self::output::TapTree;
|
||||||
|
|
|
@ -25,12 +25,23 @@ use util::psbt::map::Map;
|
||||||
use util::psbt::raw;
|
use util::psbt::raw;
|
||||||
use util::psbt::Error;
|
use util::psbt::Error;
|
||||||
|
|
||||||
|
use schnorr;
|
||||||
|
use util::taproot::TapLeafHash;
|
||||||
|
|
||||||
|
use util::taproot::{NodeInfo, TaprootBuilder};
|
||||||
|
|
||||||
/// Type: Redeem Script PSBT_OUT_REDEEM_SCRIPT = 0x00
|
/// Type: Redeem Script PSBT_OUT_REDEEM_SCRIPT = 0x00
|
||||||
const PSBT_OUT_REDEEM_SCRIPT: u8 = 0x00;
|
const PSBT_OUT_REDEEM_SCRIPT: u8 = 0x00;
|
||||||
/// Type: Witness Script PSBT_OUT_WITNESS_SCRIPT = 0x01
|
/// Type: Witness Script PSBT_OUT_WITNESS_SCRIPT = 0x01
|
||||||
const PSBT_OUT_WITNESS_SCRIPT: u8 = 0x01;
|
const PSBT_OUT_WITNESS_SCRIPT: u8 = 0x01;
|
||||||
/// Type: BIP 32 Derivation Path PSBT_OUT_BIP32_DERIVATION = 0x02
|
/// Type: BIP 32 Derivation Path PSBT_OUT_BIP32_DERIVATION = 0x02
|
||||||
const PSBT_OUT_BIP32_DERIVATION: u8 = 0x02;
|
const PSBT_OUT_BIP32_DERIVATION: u8 = 0x02;
|
||||||
|
/// Type: Taproot Internal Key PSBT_OUT_TAP_INTERNAL_KEY = 0x05
|
||||||
|
const PSBT_OUT_TAP_INTERNAL_KEY: u8 = 0x05;
|
||||||
|
/// Type: Taproot Tree PSBT_OUT_TAP_TREE = 0x06
|
||||||
|
const PSBT_OUT_TAP_TREE: u8 = 0x06;
|
||||||
|
/// Type: Taproot Key BIP 32 Derivation Path PSBT_OUT_TAP_BIP32_DERIVATION = 0x07
|
||||||
|
const PSBT_OUT_TAP_BIP32_DERIVATION: u8 = 0x07;
|
||||||
/// Type: Proprietary Use Type PSBT_IN_PROPRIETARY = 0xFC
|
/// Type: Proprietary Use Type PSBT_IN_PROPRIETARY = 0xFC
|
||||||
const PSBT_OUT_PROPRIETARY: u8 = 0xFC;
|
const PSBT_OUT_PROPRIETARY: u8 = 0xFC;
|
||||||
|
|
||||||
|
@ -47,14 +58,67 @@ pub struct Output {
|
||||||
/// corresponding master key fingerprints and derivation paths.
|
/// corresponding master key fingerprints and derivation paths.
|
||||||
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq"))]
|
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq"))]
|
||||||
pub bip32_derivation: BTreeMap<PublicKey, KeySource>,
|
pub bip32_derivation: BTreeMap<PublicKey, KeySource>,
|
||||||
|
/// The internal pubkey
|
||||||
|
pub tap_internal_key: Option<schnorr::PublicKey>,
|
||||||
|
/// Taproot Output tree
|
||||||
|
pub tap_tree: Option<TapTree>,
|
||||||
|
/// Map of tap root x only keys to origin info and leaf hashes contained in it
|
||||||
|
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq"))]
|
||||||
|
pub tap_key_origins: BTreeMap<schnorr::PublicKey, (Vec<TapLeafHash>, KeySource)>,
|
||||||
/// Proprietary key-value pairs for this output.
|
/// Proprietary key-value pairs for this output.
|
||||||
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq_byte_values"))]
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(with = "::serde_utils::btreemap_as_seq_byte_values")
|
||||||
|
)]
|
||||||
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
|
pub proprietary: BTreeMap<raw::ProprietaryKey, Vec<u8>>,
|
||||||
/// Unknown key-value pairs for this output.
|
/// Unknown key-value pairs for this output.
|
||||||
#[cfg_attr(feature = "serde", serde(with = "::serde_utils::btreemap_as_seq_byte_values"))]
|
#[cfg_attr(
|
||||||
|
feature = "serde",
|
||||||
|
serde(with = "::serde_utils::btreemap_as_seq_byte_values")
|
||||||
|
)]
|
||||||
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
pub unknown: BTreeMap<raw::Key, Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Taproot Tree representing a finalized [`TaprootBuilder`] (a complete binary tree)
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct TapTree(pub(crate) TaprootBuilder);
|
||||||
|
|
||||||
|
impl PartialEq for TapTree {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.node_info().hash.eq(&other.node_info().hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for TapTree {}
|
||||||
|
|
||||||
|
impl TapTree {
|
||||||
|
// get the inner node info as the builder is finalized
|
||||||
|
fn node_info(&self) -> &NodeInfo {
|
||||||
|
// The builder algorithm invariant guarantees that is_complete builder
|
||||||
|
// have only 1 element in branch and that is not None.
|
||||||
|
// We make sure that we only allow is_complete builders via the from_inner
|
||||||
|
// constructor
|
||||||
|
self.0.branch()[0].as_ref().expect("from_inner only parses is_complete builders")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert a [`TaprootBuilder`] into a tree if it is complete binary tree.
|
||||||
|
/// Returns the inner as Err if it is not a complete tree
|
||||||
|
pub fn from_inner(inner: TaprootBuilder) -> Result<Self, TaprootBuilder> {
|
||||||
|
if inner.is_complete() {
|
||||||
|
Ok(TapTree(inner))
|
||||||
|
} else {
|
||||||
|
Err(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convert self into builder [`TaprootBuilder`]. The builder is guaranteed to
|
||||||
|
/// be finalized.
|
||||||
|
pub fn into_inner(self) -> TaprootBuilder {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Map for Output {
|
impl Map for Output {
|
||||||
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> {
|
fn insert_pair(&mut self, pair: raw::Pair) -> Result<(), encode::Error> {
|
||||||
let raw::Pair {
|
let raw::Pair {
|
||||||
|
@ -82,11 +146,30 @@ impl Map for Output {
|
||||||
btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
||||||
btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key).into()),
|
btree_map::Entry::Occupied(_) => return Err(Error::DuplicateKey(raw_key).into()),
|
||||||
}
|
}
|
||||||
_ => match self.unknown.entry(raw_key) {
|
PSBT_OUT_TAP_INTERNAL_KEY => {
|
||||||
btree_map::Entry::Vacant(empty_key) => {empty_key.insert(raw_value);},
|
impl_psbt_insert_pair! {
|
||||||
btree_map::Entry::Occupied(k) => return Err(Error::DuplicateKey(k.key().clone()).into()),
|
self.tap_internal_key <= <raw_key: _>|<raw_value: schnorr::PublicKey>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
PSBT_OUT_TAP_TREE => {
|
||||||
|
impl_psbt_insert_pair! {
|
||||||
|
self.tap_tree <= <raw_key: _>|<raw_value: TapTree>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PSBT_OUT_TAP_BIP32_DERIVATION => {
|
||||||
|
impl_psbt_insert_pair! {
|
||||||
|
self.tap_key_origins <= <raw_key: schnorr::PublicKey>|< raw_value: (Vec<TapLeafHash>, KeySource)>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => match self.unknown.entry(raw_key) {
|
||||||
|
btree_map::Entry::Vacant(empty_key) => {
|
||||||
|
empty_key.insert(raw_value);
|
||||||
|
}
|
||||||
|
btree_map::Entry::Occupied(k) => {
|
||||||
|
return Err(Error::DuplicateKey(k.key().clone()).into())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -106,6 +189,19 @@ impl Map for Output {
|
||||||
rv.push(self.bip32_derivation as <PSBT_OUT_BIP32_DERIVATION, PublicKey>|<KeySource>)
|
rv.push(self.bip32_derivation as <PSBT_OUT_BIP32_DERIVATION, PublicKey>|<KeySource>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl_psbt_get_pair! {
|
||||||
|
rv.push(self.tap_internal_key as <PSBT_OUT_TAP_INTERNAL_KEY, _>|<schnorr::PublicKey>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_psbt_get_pair! {
|
||||||
|
rv.push(self.tap_tree as <PSBT_OUT_TAP_TREE, _>|<TaprootBuilder>)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_psbt_get_pair! {
|
||||||
|
rv.push(self.tap_key_origins as <PSBT_OUT_TAP_BIP32_DERIVATION,
|
||||||
|
schnorr::PublicKey>|<(Vec<TapLeafHash>, KeySource)>)
|
||||||
|
}
|
||||||
|
|
||||||
for (key, value) in self.proprietary.iter() {
|
for (key, value) in self.proprietary.iter() {
|
||||||
rv.push(raw::Pair {
|
rv.push(raw::Pair {
|
||||||
key: key.to_key(),
|
key: key.to_key(),
|
||||||
|
@ -127,9 +223,12 @@ impl Map for Output {
|
||||||
self.bip32_derivation.extend(other.bip32_derivation);
|
self.bip32_derivation.extend(other.bip32_derivation);
|
||||||
self.proprietary.extend(other.proprietary);
|
self.proprietary.extend(other.proprietary);
|
||||||
self.unknown.extend(other.unknown);
|
self.unknown.extend(other.unknown);
|
||||||
|
self.tap_key_origins.extend(other.tap_key_origins);
|
||||||
|
|
||||||
merge!(redeem_script, self, other);
|
merge!(redeem_script, self, other);
|
||||||
merge!(witness_script, self, other);
|
merge!(witness_script, self, other);
|
||||||
|
merge!(tap_internal_key, self, other);
|
||||||
|
merge!(tap_tree, self, other);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ mod macros;
|
||||||
pub mod serialize;
|
pub mod serialize;
|
||||||
|
|
||||||
mod map;
|
mod map;
|
||||||
pub use self::map::{Map, Input, Output};
|
pub use self::map::{Map, Input, Output, TapTree};
|
||||||
|
|
||||||
use util::bip32::{ExtendedPubKey, KeySource};
|
use util::bip32::{ExtendedPubKey, KeySource};
|
||||||
|
|
||||||
|
@ -814,6 +814,87 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod bip_371_vectors {
|
||||||
|
use super::*;
|
||||||
|
use super::serialize;
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn invalid_vectors() {
|
||||||
|
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a075701172102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232000000").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
||||||
|
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011342173bb3d36c074afb716fec6307a069a2e450b995f3c82785945ab8df0e24260dcd703b0cbf34de399184a9481ac2b3586db6601f026a77f7e4938481bc34751701aa000000").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature len");
|
||||||
|
let err = hex_psbt!("70736274ff010071020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02787c01000000000016001483a7e34bd99ff03a4962ef8a1a101bb295461ece606b042a010000001600147ac369df1b20e033d6116623957b0ac49f3c52e8000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757221602fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000000000").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
||||||
|
let err = hex_psbt!("70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000001052102fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa23200").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
||||||
|
let err = hex_psbt!("70736274ff01007d020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff02887b0100000000001600142382871c7e8421a00093f754d91281e675874b9f606b042a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07570000220702fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da7560000800100008000000080010000000000000000").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "parse failed: Invalid xonly public key");
|
||||||
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6924214022cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094089756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "PSBT error: Hash Parse Error: bad slice length 33 (expected 32)");
|
||||||
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b094289756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb01010000").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature len");
|
||||||
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b093989756aa3739ccc689ec0fcf3a360be32cc0b59b16e93a1e8bb4605726b2ca7a3ff706c4176649632b2cc68e1f912b8a578e3719ce7710885c7a966f49bcd43cb0000").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "parse failed: Invalid Schnorr signature len");
|
||||||
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926315c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f80023202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "parse failed: Invalid control block");
|
||||||
|
let err = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a01000000225120030da4fce4f7db28c2cb2951631e003713856597fe963882cb500e68112cca63000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926115c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e123202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc00000").unwrap_err();
|
||||||
|
assert_eq!(err.to_string(), "parse failed: Invalid control block");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rtt_psbt(psbt: PartiallySignedTransaction) {
|
||||||
|
let enc = serialize(&psbt);
|
||||||
|
let psbt2 = deserialize::<PartiallySignedTransaction>(&enc).unwrap();
|
||||||
|
assert_eq!(psbt, psbt2);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn valid_psbt_vectors() {
|
||||||
|
let psbt = hex_psbt!("70736274ff010052020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a01000000160014768e1eeb4cf420866033f80aceff0f9720744969000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232002202036b772a6db74d8753c98a827958de6c78ab3312109f37d3e0304484242ece73d818772b2da7540000800100008000000080000000000000000000").unwrap();
|
||||||
|
let internal_key = psbt.inputs[0].tap_internal_key.unwrap();
|
||||||
|
assert!(psbt.inputs[0].tap_key_origins.contains_key(&internal_key));
|
||||||
|
rtt_psbt(psbt);
|
||||||
|
|
||||||
|
// vector 2
|
||||||
|
let psbt = hex_psbt!("70736274ff010052020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a01000000160014768e1eeb4cf420866033f80aceff0f9720744969000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a0757011340bb53ec917bad9d906af1ba87181c48b86ace5aae2b53605a725ca74625631476fc6f5baedaf4f2ee0f477f36f58f3970d5b8273b7e497b97af2e3f125c97af342116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232002202036b772a6db74d8753c98a827958de6c78ab3312109f37d3e0304484242ece73d818772b2da7540000800100008000000080000000000000000000").unwrap();
|
||||||
|
let internal_key = psbt.inputs[0].tap_internal_key.unwrap();
|
||||||
|
assert!(psbt.inputs[0].tap_key_origins.contains_key(&internal_key));
|
||||||
|
assert!(psbt.inputs[0].tap_key_sig.is_some());
|
||||||
|
rtt_psbt(psbt);
|
||||||
|
|
||||||
|
// vector 3
|
||||||
|
let psbt = hex_psbt!("70736274ff01005e020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa232000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000").unwrap();
|
||||||
|
let internal_key = psbt.outputs[0].tap_internal_key.unwrap();
|
||||||
|
assert!(psbt.outputs[0].tap_key_origins.contains_key(&internal_key));
|
||||||
|
rtt_psbt(psbt);
|
||||||
|
|
||||||
|
// vector 4
|
||||||
|
let psbt = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b6926215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f823202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc04215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac097c6e6fea5ff714ff5724499990810e406e98aa10f5bf7e5f6784bc1d0a9a6ce23204320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2acc06215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f82320fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9acc021162cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d23901cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09772b2da7560000800100008002000080000000000000000021164320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b23901115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f8772b2da75600008001000080010000800000000000000000211650929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2116fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca939016f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970772b2da7560000800100008003000080000000000000000001172050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0011820f0362e2f75a6f420a5bde3eb221d96ae6720cf25f81890c95b1d775acb515e65000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000").unwrap();
|
||||||
|
assert!(psbt.inputs[0].tap_internal_key.is_some());
|
||||||
|
assert!(psbt.inputs[0].tap_merkle_root.is_some());
|
||||||
|
assert!(!psbt.inputs[0].tap_key_origins.is_empty());
|
||||||
|
assert!(!psbt.inputs[0].tap_scripts.is_empty());
|
||||||
|
rtt_psbt(psbt);
|
||||||
|
|
||||||
|
// vector 5
|
||||||
|
let psbt = hex_psbt!("70736274ff01005e020000000127744ababf3027fe0d6cf23a96eee2efb188ef52301954585883e69b6624b2420000000000ffffffff0148e6052a010000002251200a8cbdc86de1ce1c0f9caeb22d6df7ced3683fe423e05d1e402a879341d6f6f5000000000001012b00f2052a010000002251205a2c2cf5b52cf31f83ad2e8da63ff03183ecd8f609c7510ae8a48e03910a07572116fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2321900772b2da75600008001000080000000800100000000000000011720fe349064c98d6e2a853fa3c9b12bd8b304a19c195c60efa7ee2393046d3fa2320001052050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac001066f02c02220736e572900fe1252589a2143c8f3c79f71a0412d2353af755e9701c782694a02ac02c02220631c5f3b5832b8fbdebfb19704ceeb323c21f40f7a24f43d68ef0cc26b125969ac01c0222044faa49a0338de488c8dfffecdfb6f329f380bd566ef20c8df6d813eab1c4273ac210744faa49a0338de488c8dfffecdfb6f329f380bd566ef20c8df6d813eab1c42733901f06b798b92a10ed9a9d0bbfd3af173a53b1617da3a4159ca008216cd856b2e0e772b2da75600008001000080010000800000000003000000210750929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2107631c5f3b5832b8fbdebfb19704ceeb323c21f40f7a24f43d68ef0cc26b125969390118ace409889785e0ea70ceebb8e1ca892a7a78eaede0f2e296cf435961a8f4ca772b2da756000080010000800200008000000000030000002107736e572900fe1252589a2143c8f3c79f71a0412d2353af755e9701c782694a02390129a5b4915090162d759afd3fe0f93fa3326056d0b4088cb933cae7826cb8d82c772b2da7560000800100008003000080000000000300000000").unwrap();
|
||||||
|
assert!(psbt.outputs[0].tap_internal_key.is_some());
|
||||||
|
assert!(!psbt.outputs[0].tap_key_origins.is_empty());
|
||||||
|
assert!(psbt.outputs[0].tap_tree.is_some());
|
||||||
|
rtt_psbt(psbt);
|
||||||
|
|
||||||
|
// vector 6
|
||||||
|
let psbt = hex_psbt!("70736274ff01005e02000000019bd48765230bf9a72e662001f972556e54f0c6f97feb56bcb5600d817f6995260100000000ffffffff0148e6052a0100000022512083698e458c6664e1595d75da2597de1e22ee97d798e706c4c0a4b5a9823cd743000000000001012b00f2052a01000000225120c2247efbfd92ac47f6f40b8d42d169175a19fa9fa10e4a25d7f35eb4dd85b69241142cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b0940bf818d9757d6ffeb538ba057fb4c1fc4e0f5ef186e765beb564791e02af5fd3d5e2551d4e34e33d86f276b82c99c79aed3f0395a081efcd2cc2c65dd7e693d7941144320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f840e1f1ab6fabfa26b236f21833719dc1d428ab768d80f91f9988d8abef47bfb863bb1f2a529f768c15f00ce34ec283cdc07e88f8428be28f6ef64043c32911811a4114fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca96f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae97040ec1f0379206461c83342285423326708ab031f0da4a253ee45aafa5b8c92034d8b605490f8cd13e00f989989b97e215faa36f12dee3693d2daccf3781c1757f66215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac06f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f823202cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d2acc04215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac097c6e6fea5ff714ff5724499990810e406e98aa10f5bf7e5f6784bc1d0a9a6ce23204320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b2acc06215c150929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f82320fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca9acc021162cb13ac68248de806aa6a3659cf3c03eb6821d09c8114a4e868febde865bb6d23901cd970e15f53fc0c82f950fd560ffa919b76172be017368a89913af074f400b09772b2da7560000800100008002000080000000000000000021164320b0bf16f011b53ea7be615924aa7f27e5d29ad20ea1155d848676c3bad1b23901115f2e490af7cc45c4f78511f36057ce5c5a5c56325a29fb44dfc203f356e1f8772b2da75600008001000080010000800000000000000000211650929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac005007c461e5d2116fa0f7a3cef3b1d0c0a6ce7d26e17ada0b2e5c92d19efad48b41859cb8a451ca939016f7d62059e9497a1a4a267569d9876da60101aff38e3529b9b939ce7f91ae970772b2da7560000800100008003000080000000000000000001172050929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0011820f0362e2f75a6f420a5bde3eb221d96ae6720cf25f81890c95b1d775acb515e65000105201124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e67121071124da7aec92ccd06c954562647f437b138b95721a84be2bf2276bbddab3e6711900772b2da7560000800100008000000080000000000500000000").unwrap();
|
||||||
|
assert!(psbt.inputs[0].tap_internal_key.is_some());
|
||||||
|
assert!(psbt.inputs[0].tap_merkle_root.is_some());
|
||||||
|
assert!(!psbt.inputs[0].tap_scripts.is_empty());
|
||||||
|
assert!(!psbt.inputs[0].tap_script_sigs.is_empty());
|
||||||
|
assert!(!psbt.inputs[0].tap_key_origins.is_empty());
|
||||||
|
rtt_psbt(psbt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_and_deserialize_preimage_psbt(){
|
fn serialize_and_deserialize_preimage_psbt(){
|
||||||
// create a sha preimage map
|
// create a sha preimage map
|
||||||
|
|
|
@ -24,12 +24,18 @@ use io;
|
||||||
|
|
||||||
use blockdata::script::Script;
|
use blockdata::script::Script;
|
||||||
use blockdata::transaction::{EcdsaSigHashType, Transaction, TxOut};
|
use blockdata::transaction::{EcdsaSigHashType, Transaction, TxOut};
|
||||||
use consensus::encode::{self, serialize, Decodable};
|
use consensus::encode::{self, serialize, Decodable, Encodable, deserialize_partial};
|
||||||
|
use secp256k1::schnorrsig;
|
||||||
use util::bip32::{ChildNumber, Fingerprint, KeySource};
|
use util::bip32::{ChildNumber, Fingerprint, KeySource};
|
||||||
use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
use hashes::{hash160, ripemd160, sha256, sha256d, Hash};
|
||||||
use util::ecdsa::PublicKey;
|
use util::ecdsa::PublicKey;
|
||||||
use util::psbt;
|
use util::psbt;
|
||||||
|
use util::taproot::{TapBranchHash, TapLeafHash, ControlBlock, LeafVersion};
|
||||||
|
use schnorr;
|
||||||
|
use super::map::TapTree;
|
||||||
|
|
||||||
|
use util::taproot::TaprootBuilder;
|
||||||
|
use util::sighash::SchnorrSigHashType;
|
||||||
/// A trait for serializing a value as raw data for insertion into PSBT
|
/// A trait for serializing a value as raw data for insertion into PSBT
|
||||||
/// key-value pairs.
|
/// key-value pairs.
|
||||||
pub trait Serialize {
|
pub trait Serialize {
|
||||||
|
@ -48,9 +54,14 @@ impl_psbt_de_serialize!(TxOut);
|
||||||
impl_psbt_de_serialize!(Vec<Vec<u8>>); // scriptWitness
|
impl_psbt_de_serialize!(Vec<Vec<u8>>); // scriptWitness
|
||||||
impl_psbt_hash_de_serialize!(ripemd160::Hash);
|
impl_psbt_hash_de_serialize!(ripemd160::Hash);
|
||||||
impl_psbt_hash_de_serialize!(sha256::Hash);
|
impl_psbt_hash_de_serialize!(sha256::Hash);
|
||||||
|
impl_psbt_hash_de_serialize!(TapLeafHash);
|
||||||
|
impl_psbt_hash_de_serialize!(TapBranchHash);
|
||||||
impl_psbt_hash_de_serialize!(hash160::Hash);
|
impl_psbt_hash_de_serialize!(hash160::Hash);
|
||||||
impl_psbt_hash_de_serialize!(sha256d::Hash);
|
impl_psbt_hash_de_serialize!(sha256d::Hash);
|
||||||
|
|
||||||
|
// taproot
|
||||||
|
impl_psbt_de_serialize!(Vec<TapLeafHash>);
|
||||||
|
|
||||||
impl Serialize for Script {
|
impl Serialize for Script {
|
||||||
fn serialize(&self) -> Vec<u8> {
|
fn serialize(&self) -> Vec<u8> {
|
||||||
self.to_bytes()
|
self.to_bytes()
|
||||||
|
@ -80,7 +91,7 @@ impl Deserialize for PublicKey {
|
||||||
|
|
||||||
impl Serialize for KeySource {
|
impl Serialize for KeySource {
|
||||||
fn serialize(&self) -> Vec<u8> {
|
fn serialize(&self) -> Vec<u8> {
|
||||||
let mut rv: Vec<u8> = Vec::with_capacity(4 + 4 * (self.1).as_ref().len());
|
let mut rv: Vec<u8> = Vec::with_capacity(key_source_len(&self));
|
||||||
|
|
||||||
rv.append(&mut self.0.to_bytes().to_vec());
|
rv.append(&mut self.0.to_bytes().to_vec());
|
||||||
|
|
||||||
|
@ -144,3 +155,170 @@ impl Deserialize for EcdsaSigHashType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Taproot related ser/deser
|
||||||
|
impl Serialize for schnorr::PublicKey {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
schnorr::PublicKey::serialize(&self).to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserialize for schnorr::PublicKey {
|
||||||
|
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
|
||||||
|
schnorr::PublicKey::from_slice(bytes)
|
||||||
|
.map_err(|_| encode::Error::ParseFailed("Invalid xonly public key"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for schnorr::SchnorrSig {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
self.to_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserialize for schnorr::SchnorrSig {
|
||||||
|
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
|
||||||
|
match bytes.len() {
|
||||||
|
65 => {
|
||||||
|
let hash_ty = SchnorrSigHashType::from_u8(bytes[64])
|
||||||
|
.map_err(|_| encode::Error::ParseFailed("Invalid Sighash type"))?;
|
||||||
|
let sig = schnorrsig::Signature::from_slice(&bytes[..64])
|
||||||
|
.map_err(|_| encode::Error::ParseFailed("Invalid Schnorr signature"))?;
|
||||||
|
Ok(schnorr::SchnorrSig{ sig, hash_ty })
|
||||||
|
}
|
||||||
|
64 => {
|
||||||
|
let sig = schnorrsig::Signature::from_slice(&bytes[..64])
|
||||||
|
.map_err(|_| encode::Error::ParseFailed("Invalid Schnorr signature"))?;
|
||||||
|
Ok(schnorr::SchnorrSig{ sig, hash_ty: SchnorrSigHashType::Default })
|
||||||
|
}
|
||||||
|
_ => Err(encode::Error::ParseFailed("Invalid Schnorr signature len"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for (schnorr::PublicKey, TapLeafHash) {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let ser_pk = self.0.serialize();
|
||||||
|
let mut buf = Vec::with_capacity(ser_pk.len() + self.1.as_ref().len());
|
||||||
|
buf.extend(&ser_pk);
|
||||||
|
buf.extend(self.1.as_ref());
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserialize for (schnorr::PublicKey, TapLeafHash) {
|
||||||
|
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
|
||||||
|
if bytes.len() < 32 {
|
||||||
|
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())
|
||||||
|
}
|
||||||
|
let a: schnorr::PublicKey = Deserialize::deserialize(&bytes[..32])?;
|
||||||
|
let b: TapLeafHash = Deserialize::deserialize(&bytes[32..])?;
|
||||||
|
Ok((a, b))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for ControlBlock {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
ControlBlock::serialize(&self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserialize for ControlBlock {
|
||||||
|
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
|
||||||
|
Self::from_slice(bytes)
|
||||||
|
.map_err(|_| encode::Error::ParseFailed("Invalid control block"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Versioned Script
|
||||||
|
impl Serialize for (Script, LeafVersion) {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut buf = Vec::with_capacity(self.0.len() + 1);
|
||||||
|
buf.extend(self.0.as_bytes());
|
||||||
|
buf.push(self.1.as_u8());
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserialize for (Script, LeafVersion) {
|
||||||
|
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
|
||||||
|
if bytes.is_empty() {
|
||||||
|
return Err(io::Error::from(io::ErrorKind::UnexpectedEof).into())
|
||||||
|
}
|
||||||
|
// The last byte is LeafVersion.
|
||||||
|
let script = Script::deserialize(&bytes[..bytes.len() - 1])?;
|
||||||
|
let leaf_ver = LeafVersion::from_u8(bytes[bytes.len() - 1])
|
||||||
|
.map_err(|_| encode::Error::ParseFailed("invalid leaf version"))?;
|
||||||
|
Ok((script, leaf_ver))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl Serialize for (Vec<TapLeafHash>, KeySource) {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
let mut buf = Vec::with_capacity( 32 * self.0.len() + key_source_len(&self.1));
|
||||||
|
self.0.consensus_encode(&mut buf).expect("Vecs don't error allocation");
|
||||||
|
// TODO: Add support for writing into a writer for key-source
|
||||||
|
buf.extend(self.1.serialize());
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserialize for (Vec<TapLeafHash>, KeySource) {
|
||||||
|
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
|
||||||
|
let (leafhash_vec, consumed) = deserialize_partial::<Vec::<TapLeafHash>>(&bytes)?;
|
||||||
|
let key_source = KeySource::deserialize(&bytes[consumed..])?;
|
||||||
|
Ok((leafhash_vec, key_source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for TapTree {
|
||||||
|
fn serialize(&self) -> Vec<u8> {
|
||||||
|
match (self.0.branch().len(), self.0.branch().last()) {
|
||||||
|
(1, Some(Some(root))) => {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
for leaf_info in root.leaves.iter() {
|
||||||
|
// # Cast Safety:
|
||||||
|
//
|
||||||
|
// TaprootMerkleBranch can only have len atmost 128(TAPROOT_CONTROL_MAX_NODE_COUNT).
|
||||||
|
// safe to cast from usize to u8
|
||||||
|
buf.push(leaf_info.merkle_branch.as_inner().len() as u8);
|
||||||
|
buf.push(leaf_info.ver.as_u8());
|
||||||
|
leaf_info.script.consensus_encode(&mut buf).expect("Vecs dont err");
|
||||||
|
}
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
// This should be unreachable as we Taptree is already finalized
|
||||||
|
_ => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserialize for TapTree {
|
||||||
|
fn deserialize(bytes: &[u8]) -> Result<Self, encode::Error> {
|
||||||
|
let mut builder = TaprootBuilder::new();
|
||||||
|
let mut bytes_iter = bytes.iter();
|
||||||
|
while let Some(depth) = bytes_iter.next() {
|
||||||
|
let version = bytes_iter.next().ok_or(encode::Error::ParseFailed("Invalid Taproot Builder"))?;
|
||||||
|
let (script, consumed) = deserialize_partial::<Script>(bytes_iter.as_slice())?;
|
||||||
|
if consumed > 0 {
|
||||||
|
bytes_iter.nth(consumed - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let leaf_version = LeafVersion::from_u8(*version)
|
||||||
|
.map_err(|_| encode::Error::ParseFailed("Leaf Version Error"))?;
|
||||||
|
builder = builder.add_leaf_with_ver(usize::from(*depth), script, leaf_version)
|
||||||
|
.map_err(|_| encode::Error::ParseFailed("Tree not in DFS order"))?;
|
||||||
|
}
|
||||||
|
if builder.is_complete() {
|
||||||
|
Ok(TapTree(builder))
|
||||||
|
} else {
|
||||||
|
Err(encode::Error::ParseFailed("Incomplete taproot Tree"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to compute key source len
|
||||||
|
fn key_source_len(key_source: &KeySource) -> usize {
|
||||||
|
4 + 4 * (key_source.1).as_ref().len()
|
||||||
|
}
|
||||||
|
|
|
@ -110,6 +110,7 @@ impl TweakedPublicKey {
|
||||||
|
|
||||||
/// A BIP340-341 serialized schnorr signature with the corresponding hash type.
|
/// A BIP340-341 serialized schnorr signature with the corresponding hash type.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct SchnorrSig {
|
pub struct SchnorrSig {
|
||||||
/// The underlying schnorr signature
|
/// The underlying schnorr signature
|
||||||
pub sig: secp256k1::schnorrsig::Signature,
|
pub sig: secp256k1::schnorrsig::Signature,
|
||||||
|
|
|
@ -104,6 +104,7 @@ pub struct ScriptPath<'s> {
|
||||||
/// Hashtype of an input's signature, encoded in the last byte of the signature
|
/// Hashtype of an input's signature, encoded in the last byte of the signature
|
||||||
/// Fixed values so they can be casted as integer types for encoding
|
/// Fixed values so they can be casted as integer types for encoding
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub enum SchnorrSigHashType {
|
pub enum SchnorrSigHashType {
|
||||||
/// 0x0: Used when not explicitly specified, defaulting to [`SchnorrSigHashType::All`]
|
/// 0x0: Used when not explicitly specified, defaulting to [`SchnorrSigHashType::All`]
|
||||||
Default = 0x00,
|
Default = 0x00,
|
||||||
|
|
|
@ -68,6 +68,7 @@ macro_rules! sha256t_hash_newtype {
|
||||||
#[doc = "The tag used for ["]
|
#[doc = "The tag used for ["]
|
||||||
#[doc = $sname]
|
#[doc = $sname]
|
||||||
#[doc = "]"]
|
#[doc = "]"]
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
|
||||||
pub struct $tag;
|
pub struct $tag;
|
||||||
|
|
||||||
impl sha256t::Tag for $tag {
|
impl sha256t::Tag for $tag {
|
||||||
|
@ -345,6 +346,7 @@ impl TaprootSpendInfo {
|
||||||
/// branches in a DFS(Depth first search) walk to construct this tree.
|
/// branches in a DFS(Depth first search) walk to construct this tree.
|
||||||
// Similar to Taproot Builder in bitcoin core
|
// Similar to Taproot Builder in bitcoin core
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct TaprootBuilder {
|
pub struct TaprootBuilder {
|
||||||
// The following doc-comment is from bitcoin core, but modified for rust
|
// The following doc-comment is from bitcoin core, but modified for rust
|
||||||
// The comment below describes the current state of the builder for a given tree.
|
// The comment below describes the current state of the builder for a given tree.
|
||||||
|
@ -418,6 +420,11 @@ impl TaprootBuilder {
|
||||||
self.insert(node, depth)
|
self.insert(node, depth)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if the builder is a complete tree
|
||||||
|
pub fn is_complete(&self) -> bool {
|
||||||
|
self.branch.len() == 1 && self.branch[0].is_some()
|
||||||
|
}
|
||||||
|
|
||||||
/// Create [`TaprootSpendInfo`] with the given internal key
|
/// Create [`TaprootSpendInfo`] with the given internal key
|
||||||
pub fn finalize<C: secp256k1::Verification>(
|
pub fn finalize<C: secp256k1::Verification>(
|
||||||
mut self,
|
mut self,
|
||||||
|
@ -435,6 +442,10 @@ impl TaprootBuilder {
|
||||||
Ok(TaprootSpendInfo::from_node_info(secp, internal_key, node))
|
Ok(TaprootSpendInfo::from_node_info(secp, internal_key, node))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn branch(&self) -> &[Option<NodeInfo>]{
|
||||||
|
&self.branch
|
||||||
|
}
|
||||||
|
|
||||||
// Helper function to insert a leaf at a depth
|
// Helper function to insert a leaf at a depth
|
||||||
fn insert(mut self, mut node: NodeInfo, mut depth: usize) -> Result<Self, TaprootBuilderError> {
|
fn insert(mut self, mut node: NodeInfo, mut depth: usize) -> Result<Self, TaprootBuilderError> {
|
||||||
// early error on invalid depth. Though this will be checked later
|
// early error on invalid depth. Though this will be checked later
|
||||||
|
@ -484,11 +495,12 @@ impl TaprootBuilder {
|
||||||
|
|
||||||
// Internally used structure to represent the node information in taproot tree
|
// Internally used structure to represent the node information in taproot tree
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
struct NodeInfo {
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub(crate) struct NodeInfo {
|
||||||
/// Merkle Hash for this node
|
/// Merkle Hash for this node
|
||||||
hash: sha256::Hash,
|
pub(crate) hash: sha256::Hash,
|
||||||
/// information about leaves inside this node
|
/// information about leaves inside this node
|
||||||
leaves: Vec<LeafInfo>,
|
pub(crate) leaves: Vec<LeafInfo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NodeInfo {
|
impl NodeInfo {
|
||||||
|
@ -537,13 +549,14 @@ impl NodeInfo {
|
||||||
|
|
||||||
// Internally used structure to store information about taproot leaf node
|
// Internally used structure to store information about taproot leaf node
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
struct LeafInfo {
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub(crate) struct LeafInfo {
|
||||||
// The underlying script
|
// The underlying script
|
||||||
script: Script,
|
pub(crate) script: Script,
|
||||||
// The leaf version
|
// The leaf version
|
||||||
ver: LeafVersion,
|
pub(crate) ver: LeafVersion,
|
||||||
// The merkle proof(hashing partners) to get this node
|
// The merkle proof(hashing partners) to get this node
|
||||||
merkle_branch: TaprootMerkleBranch,
|
pub(crate) merkle_branch: TaprootMerkleBranch,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LeafInfo {
|
impl LeafInfo {
|
||||||
|
@ -567,6 +580,7 @@ impl LeafInfo {
|
||||||
// The type of hash is sha256::Hash because the vector might contain
|
// The type of hash is sha256::Hash because the vector might contain
|
||||||
// both TapBranchHash and TapLeafHash
|
// both TapBranchHash and TapLeafHash
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct TaprootMerkleBranch(Vec<sha256::Hash>);
|
pub struct TaprootMerkleBranch(Vec<sha256::Hash>);
|
||||||
|
|
||||||
impl TaprootMerkleBranch {
|
impl TaprootMerkleBranch {
|
||||||
|
@ -638,6 +652,7 @@ impl TaprootMerkleBranch {
|
||||||
|
|
||||||
/// Control Block data structure used in Tapscript satisfaction
|
/// Control Block data structure used in Tapscript satisfaction
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct ControlBlock {
|
pub struct ControlBlock {
|
||||||
/// The tapleaf version,
|
/// The tapleaf version,
|
||||||
pub leaf_version: LeafVersion,
|
pub leaf_version: LeafVersion,
|
||||||
|
@ -744,6 +759,7 @@ impl ControlBlock {
|
||||||
|
|
||||||
/// The leaf version for tapleafs
|
/// The leaf version for tapleafs
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct LeafVersion(u8);
|
pub struct LeafVersion(u8);
|
||||||
|
|
||||||
impl Default for LeafVersion {
|
impl Default for LeafVersion {
|
||||||
|
|
Loading…
Reference in New Issue