Merge rust-bitcoin/rust-bitcoin#4410: Wrap secp256k1::XOnlyPublicKey to improve error handling

c11772a768 Accept flexible input types for Taproot-related functions (Erick Cestari)
2a518d62e6 Wrap secp256k1::XOnlyPublicKey to improve error handling (Erick Cestari)

Pull request description:

  This PR addresses issue #4361 by creating a wrapper type for XOnlyPublicKey instead of directly re-exporting it from the secp256k1 library.

  ### Key Changes

  1. Created a new `XOnlyPublicKey` struct that wraps `secp256k1::XOnlyPublicKey`
  2. Implemented custom error types:
      - `ParseXOnlyPublicKeyError` for handling parsing errors
      - `TweakXOnlyPublicKeyError` for tweaking an `XOnlyPublicKey`
  3. Updated all imports and usage throughout the codebase
  4. Implemented necessary traits and methods for compatibility

  Closes #4361

ACKs for top commit:
  apoelstra:
    ACK c11772a768eefd89dcc0e3b1a369d535c191f94a; successfully ran local tests
  tcharding:
    ACK c11772a768

Tree-SHA512: c8da3486e7ffcab6c24cc08f9b2f964dd9158449ef2bd720e54d56176bc7027052314ea23cac3f673d217fa785238ea8a9b5323ba57f02199f20e56df5893965
This commit is contained in:
merge-script 2025-05-07 17:01:31 +00:00
commit 7d4b40dfd4
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
15 changed files with 216 additions and 53 deletions

View File

@ -113,10 +113,11 @@ fn receivers_address() -> Address {
///
/// This output is locked to keys that we control, in a real application this would be a valid
/// output taken from a transaction that appears in the chain.
fn dummy_unspent_transaction_output<C: Verification>(
fn dummy_unspent_transaction_output<C: Verification, K: Into<UntweakedPublicKey>>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
internal_key: K,
) -> (OutPoint, TxOut) {
let internal_key = internal_key.into();
let script_pubkey = ScriptBuf::new_p2tr(secp, internal_key, None);
let out_point = OutPoint {

View File

@ -85,11 +85,12 @@ fn get_internal_address_xpriv<C: Signing>(
}
// Get the Taproot Key Origin.
fn get_tap_key_origin(
x_only_key: UntweakedPublicKey,
fn get_tap_key_origin<K: Into<UntweakedPublicKey> + std::cmp::Ord>(
x_only_key: K,
master_fingerprint: Fingerprint,
path: DerivationPath,
) -> BTreeMap<XOnlyPublicKey, (Vec<TapLeafHash>, (Fingerprint, DerivationPath))> {
let x_only_key = x_only_key.into();
let mut map = BTreeMap::new();
map.insert(x_only_key, (vec![], (master_fingerprint, path)));
map
@ -212,14 +213,14 @@ fn main() {
Input {
witness_utxo: Some(utxos[0].clone()),
tap_key_origins: origins[0].clone(),
tap_internal_key: Some(pk_input_1),
tap_internal_key: Some(pk_input_1.into()),
sighash_type: Some(ty),
..Default::default()
},
Input {
witness_utxo: Some(utxos[1].clone()),
tap_key_origins: origins[1].clone(),
tap_internal_key: Some(pk_input_2),
tap_internal_key: Some(pk_input_2.into()),
sighash_type: Some(ty),
..Default::default()
},

View File

@ -444,7 +444,7 @@ impl BenefactorWallet {
(vec![leaf_hash], (self.beneficiary_xpub.fingerprint(), derivation_path.clone())),
);
origins.insert(
internal_keypair.x_only_public_key().0,
internal_keypair.x_only_public_key().0.into(),
(vec![], (self.master_xpriv.fingerprint(&self.secp), derivation_path)),
);
let ty = "SIGHASH_ALL".parse::<PsbtSighashType>()?;
@ -459,7 +459,7 @@ impl BenefactorWallet {
tap_key_origins: origins,
tap_merkle_root: taproot_spend_info.merkle_root(),
sighash_type: Some(ty),
tap_internal_key: Some(internal_keypair.x_only_public_key().0),
tap_internal_key: Some(internal_keypair.x_only_public_key().0.into()),
tap_scripts,
..Default::default()
};
@ -612,7 +612,7 @@ impl BenefactorWallet {
tap_key_origins: origins,
tap_merkle_root: taproot_spend_info.merkle_root(),
sighash_type: Some(ty),
tap_internal_key: Some(new_internal_keypair.x_only_public_key().0),
tap_internal_key: Some(new_internal_keypair.x_only_public_key().0.into()),
tap_scripts,
..Default::default()
};

View File

@ -50,7 +50,7 @@ use bech32::primitives::gf32::Fe32;
use bech32::primitives::hrp::Hrp;
use hashes::{hash160, HashEngine};
use internals::array::ArrayExt;
use secp256k1::{Secp256k1, Verification, XOnlyPublicKey};
use secp256k1::{Secp256k1, Verification};
use crate::address::script_pubkey::ScriptBufExt as _;
use crate::constants::{
@ -59,6 +59,7 @@ use crate::constants::{
};
use crate::crypto::key::{
CompressedPublicKey, PubkeyHash, PublicKey, TweakedPublicKey, UntweakedPublicKey,
XOnlyPublicKey,
};
use crate::network::{Network, NetworkKind, Params};
use crate::prelude::{String, ToOwned};
@ -572,12 +573,13 @@ impl Address {
}
/// Constructs a new pay-to-Taproot (P2TR) [`Address`] from an untweaked key.
pub fn p2tr<C: Verification>(
pub fn p2tr<C: Verification, K: Into<UntweakedPublicKey>>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
internal_key: K,
merkle_root: Option<TapNodeHash>,
hrp: impl Into<KnownHrp>,
) -> Address {
let internal_key = internal_key.into();
let program = WitnessProgram::p2tr(secp, internal_key, merkle_root);
Address::from_witness_program(program, hrp)
}

View File

@ -49,11 +49,12 @@ define_extension_trait! {
/// Computes P2TR output with a given internal key and a single script spending path equal to
/// the current script, assuming that the script is a Tapscript.
fn to_p2tr<C: Verification>(
fn to_p2tr<C: Verification, K: Into<UntweakedPublicKey>>(
&self,
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
internal_key: K,
) -> ScriptBuf {
let internal_key = internal_key.into();
let leaf_hash = self.tapscript_leaf_hash();
let merkle_root = TapNodeHash::from(leaf_hash);
ScriptBuf::new_p2tr(secp, internal_key, Some(merkle_root))
@ -157,11 +158,12 @@ define_extension_trait! {
/// Generates P2TR for script spending path using an internal public key and some optional
/// script tree Merkle root.
fn new_p2tr<C: Verification>(
fn new_p2tr<C: Verification, K: Into<UntweakedPublicKey>>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
internal_key: K,
merkle_root: Option<TapNodeHash>,
) -> Self {
let internal_key = internal_key.into();
let (output_key, _) = internal_key.tap_tweak(secp, merkle_root);
// output key is 32 bytes long, so it's safe to use `new_witness_program_unchecked` (Segwitv1)
new_witness_program_unchecked(WitnessVersion::V1, output_key.serialize())

View File

@ -13,9 +13,9 @@ use core::{fmt, slice};
use hashes::{hash160, hash_newtype, sha512, Hash, HashEngine, Hmac, HmacEngine};
use internals::array::ArrayExt;
use internals::write_err;
use secp256k1::{Secp256k1, XOnlyPublicKey};
use secp256k1::Secp256k1;
use crate::crypto::key::{CompressedPublicKey, Keypair, PrivateKey};
use crate::crypto::key::{CompressedPublicKey, Keypair, PrivateKey, XOnlyPublicKey};
use crate::internal_macros::{impl_array_newtype, impl_array_newtype_stringify};
use crate::network::NetworkKind;
use crate::prelude::{String, Vec};

View File

@ -95,11 +95,12 @@ impl WitnessProgram {
///
/// This function applies BIP341 key-tweaking to the untweaked
/// key using the merkle root, if it's present.
pub fn p2tr<C: Verification>(
pub fn p2tr<C: Verification, K: Into<UntweakedPublicKey>>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
internal_key: K,
merkle_root: Option<TapNodeHash>,
) -> Self {
let internal_key = internal_key.into();
let (output_key, _parity) = internal_key.tap_tweak(secp, merkle_root);
let pubkey = output_key.as_x_only_public_key().serialize();
WitnessProgram::new_p2tr(pubkey)

View File

@ -25,11 +25,120 @@ use crate::script::{self, ScriptBuf};
use crate::taproot::{TapNodeHash, TapTweakHash};
#[rustfmt::skip] // Keep public re-exports separate.
pub use secp256k1::{constants, Keypair, Parity, Secp256k1, Verification, XOnlyPublicKey};
pub use secp256k1::{constants, Keypair, Parity, Secp256k1, Verification};
#[cfg(feature = "rand-std")]
pub use secp256k1::rand;
pub use serialized_x_only::SerializedXOnlyPublicKey;
/// A Bitcoin Schnorr X-only public key used for BIP340 signatures.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct XOnlyPublicKey(secp256k1::XOnlyPublicKey);
impl XOnlyPublicKey {
/// Constructs a new x-only public key from the provided generic Secp256k1 x-only public key.
pub fn new(key: impl Into<secp256k1::XOnlyPublicKey>) -> XOnlyPublicKey {
XOnlyPublicKey(key.into())
}
/// Creates an x-only public key from a keypair.
///
/// Returns the x-only public key and the parity of the full public key.
#[inline]
pub fn from_keypair(keypair: &Keypair) -> (XOnlyPublicKey, Parity) {
let (xonly, parity) = secp256k1::XOnlyPublicKey::from_keypair(keypair);
(XOnlyPublicKey::new(xonly), parity)
}
/// Creates an x-only public key from a 32-byte x-coordinate.
///
/// Returns an error if the provided bytes don't represent a valid secp256k1 point x-coordinate.
#[inline]
pub fn from_byte_array(
data: &[u8; constants::SCHNORR_PUBLIC_KEY_SIZE],
) -> Result<XOnlyPublicKey, ParseXOnlyPublicKeyError> {
secp256k1::XOnlyPublicKey::from_byte_array(data)
.map(XOnlyPublicKey::new)
.map_err(|_| ParseXOnlyPublicKeyError::InvalidXCoordinate)
}
/// Serializes the x-only public key as a byte-encoded x coordinate value (32 bytes).
#[inline]
pub fn serialize(&self) -> [u8; constants::SCHNORR_PUBLIC_KEY_SIZE] { self.0.serialize() }
/// Converts this x-only public key to a full public key given the parity.
#[inline]
pub fn public_key(&self, parity: Parity) -> PublicKey { self.0.public_key(parity).into() }
/// Verifies that a tweak produced by [`XOnlyPublicKey::add_tweak`] was computed correctly.
///
/// Should be called on the original untweaked key. Takes the tweaked key and output parity from
/// [`XOnlyPublicKey::add_tweak`] as input.
#[inline]
pub fn tweak_add_check<V: Verification>(
&self,
secp: &Secp256k1<V>,
tweaked_key: &Self,
tweaked_parity: Parity,
tweak: secp256k1::Scalar,
) -> bool {
self.0.tweak_add_check(secp, &tweaked_key.0, tweaked_parity, tweak)
}
/// Tweaks an [`XOnlyPublicKey`] by adding the generator multiplied with the given tweak to it.
///
/// # Returns
///
/// The newly tweaked key plus an opaque type representing the parity of the tweaked key, this
/// should be provided to `tweak_add_check` which can be used to verify a tweak more efficiently
/// than regenerating it and checking equality.
///
/// # Errors
///
/// If the resulting key would be invalid.
#[inline]
pub fn add_tweak<V: Verification>(
&self,
secp: &Secp256k1<V>,
tweak: &secp256k1::Scalar,
) -> Result<(XOnlyPublicKey, Parity), TweakXOnlyPublicKeyError> {
match self.0.add_tweak(secp, tweak) {
Ok((xonly, parity)) => Ok((XOnlyPublicKey(xonly), parity)),
Err(secp256k1::Error::InvalidTweak) => Err(TweakXOnlyPublicKeyError::BadTweak),
Err(secp256k1::Error::InvalidParityValue(_)) =>
Err(TweakXOnlyPublicKeyError::ParityError),
Err(_) => Err(TweakXOnlyPublicKeyError::ResultKeyInvalid),
}
}
}
impl FromStr for XOnlyPublicKey {
type Err = ParseXOnlyPublicKeyError;
fn from_str(s: &str) -> Result<XOnlyPublicKey, ParseXOnlyPublicKeyError> {
secp256k1::XOnlyPublicKey::from_str(s)
.map(XOnlyPublicKey::from)
.map_err(|_| ParseXOnlyPublicKeyError::InvalidXCoordinate)
}
}
impl From<secp256k1::XOnlyPublicKey> for XOnlyPublicKey {
fn from(pk: secp256k1::XOnlyPublicKey) -> XOnlyPublicKey { XOnlyPublicKey::new(pk) }
}
impl From<secp256k1::PublicKey> for XOnlyPublicKey {
fn from(pk: secp256k1::PublicKey) -> XOnlyPublicKey { XOnlyPublicKey::new(pk) }
}
impl fmt::LowerHex for XOnlyPublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::LowerHex::fmt(&self.0, f) }
}
// Allocate for serialized size
impl_to_hex_from_lower_hex!(XOnlyPublicKey, |_| constants::SCHNORR_PUBLIC_KEY_SIZE * 2);
impl fmt::Display for XOnlyPublicKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&self.0, f) }
}
/// A Bitcoin ECDSA public key.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PublicKey {
@ -222,7 +331,7 @@ impl From<secp256k1::PublicKey> for PublicKey {
}
impl From<PublicKey> for XOnlyPublicKey {
fn from(pk: PublicKey) -> XOnlyPublicKey { pk.inner.into() }
fn from(pk: PublicKey) -> XOnlyPublicKey { XOnlyPublicKey::new(pk.inner) }
}
/// An opaque return type for PublicKey::to_sort_key.
@ -874,7 +983,7 @@ impl TweakedPublicKey {
#[inline]
pub fn from_keypair(keypair: TweakedKeypair) -> Self {
let (xonly, _parity) = keypair.0.x_only_public_key();
TweakedPublicKey(xonly)
TweakedPublicKey(xonly.into())
}
/// Constructs a new [`TweakedPublicKey`] from a [`XOnlyPublicKey`]. No tweak is applied, consider
@ -933,7 +1042,7 @@ impl TweakedKeypair {
#[inline]
pub fn public_parts(&self) -> (TweakedPublicKey, Parity) {
let (xonly, parity) = self.0.x_only_public_key();
(TweakedPublicKey(xonly), parity)
(TweakedPublicKey(xonly.into()), parity)
}
}
@ -1265,7 +1374,7 @@ mod serialized_x_only {
impl SerializedXOnlyPublicKey {
/// Returns `XOnlyPublicKey` if the bytes are valid.
pub fn to_validated(self) -> Result<XOnlyPublicKey, secp256k1::Error> {
pub fn to_validated(self) -> Result<XOnlyPublicKey, ParseXOnlyPublicKeyError> {
XOnlyPublicKey::from_byte_array(self.as_byte_array())
}
}
@ -1284,6 +1393,48 @@ impl fmt::Debug for SerializedXOnlyPublicKey {
}
}
/// Error that can occur when parsing an [`XOnlyPublicKey`] from bytes.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseXOnlyPublicKeyError {
/// The provided bytes do not represent a valid secp256k1 point x-coordinate.
InvalidXCoordinate,
}
impl fmt::Display for ParseXOnlyPublicKeyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::InvalidXCoordinate => write!(f, "Invalid X coordinate for secp256k1 point"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseXOnlyPublicKeyError {}
/// Error that can occur when tweaking an [`XOnlyPublicKey`].
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TweakXOnlyPublicKeyError {
/// The tweak value was invalid.
BadTweak,
/// The resulting public key would be invalid.
ResultKeyInvalid,
/// Invalid parity value encountered during the operation.
ParityError,
}
impl fmt::Display for TweakXOnlyPublicKeyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::BadTweak => write!(f, "Invalid tweak value"),
Self::ResultKeyInvalid => write!(f, "Resulting public key would be invalid"),
Self::ParityError => write!(f, "Invalid parity value encountered"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for TweakXOnlyPublicKeyError {}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -1878,9 +1878,10 @@ mod tests {
})
}
use secp256k1::{SecretKey, XOnlyPublicKey};
use secp256k1::SecretKey;
use crate::consensus::serde as con_serde;
use crate::crypto::key::XOnlyPublicKey;
use crate::taproot::{TapNodeHash, TapTweakHash};
#[derive(serde::Deserialize)]

View File

@ -4,10 +4,9 @@ use core::fmt;
use core::str::FromStr;
use hashes::{hash160, ripemd160, sha256, sha256d};
use secp256k1::XOnlyPublicKey;
use crate::bip32::KeySource;
use crate::crypto::key::PublicKey;
use crate::crypto::key::{PublicKey, XOnlyPublicKey};
use crate::crypto::{ecdsa, taproot};
use crate::prelude::{btree_map, BTreeMap, Borrow, Box, ToOwned, Vec};
use crate::psbt::map::Map;

View File

@ -1,8 +1,7 @@
// SPDX-License-Identifier: CC0-1.0
use secp256k1::XOnlyPublicKey;
use crate::bip32::KeySource;
use crate::crypto::key::XOnlyPublicKey;
use crate::prelude::{btree_map, BTreeMap, Vec};
use crate::psbt::map::Map;
use crate::psbt::{raw, Error};

View File

@ -850,14 +850,14 @@ impl GetKey for $map<PublicKey, PrivateKey> {
match key_request {
KeyRequest::Pubkey(pk) => Ok(self.get(&pk).cloned()),
KeyRequest::XOnlyPubkey(xonly) => {
let pubkey_even = PublicKey::new(xonly.public_key(secp256k1::Parity::Even));
let pubkey_even = xonly.public_key(secp256k1::Parity::Even);
let key = self.get(&pubkey_even).cloned();
if key.is_some() {
return Ok(key);
}
let pubkey_odd = PublicKey::new(xonly.public_key(secp256k1::Parity::Odd));
let pubkey_odd = xonly.public_key(secp256k1::Parity::Odd);
if let Some(priv_key) = self.get(&pubkey_odd).copied() {
let negated_priv_key = priv_key.negate();
return Ok(Some(negated_priv_key));
@ -2267,7 +2267,7 @@ mod tests {
pubkey_map.insert(pk, priv_key);
let req_result = pubkey_map.get_key(&KeyRequest::XOnlyPubkey(xonly), &secp).unwrap();
let req_result = pubkey_map.get_key(&KeyRequest::XOnlyPubkey(xonly.into()), &secp).unwrap();
let retrieved_key = req_result.unwrap();

View File

@ -9,12 +9,11 @@ use hashes::{hash160, ripemd160, sha256, sha256d};
use internals::compact_size;
#[allow(unused)] // MSRV polyfill
use internals::slice::SliceExt;
use secp256k1::XOnlyPublicKey;
use super::map::{Input, Map, Output, PsbtSighashType};
use crate::bip32::{ChildNumber, Fingerprint, KeySource};
use crate::consensus::encode::{self, deserialize_partial, serialize, Decodable, Encodable};
use crate::crypto::key::PublicKey;
use crate::crypto::key::{PublicKey, XOnlyPublicKey};
use crate::crypto::{ecdsa, taproot};
use crate::io::Write;
use crate::prelude::{DisplayHex, String, Vec};

View File

@ -25,6 +25,7 @@ use crate::consensus::Encodable;
use crate::crypto::key::{
SerializedXOnlyPublicKey, TapTweak, TweakedPublicKey, UntweakedPublicKey,
};
use crate::key::ParseXOnlyPublicKeyError;
use crate::prelude::{BTreeMap, BTreeSet, BinaryHeap, Vec};
use crate::{Script, ScriptBuf};
@ -96,10 +97,11 @@ impl From<TapLeafHash> for TapNodeHash {
impl TapTweakHash {
/// Constructs a new BIP341 [`TapTweakHash`] from key and tweak. Produces `H_taptweak(P||R)` where
/// `P` is the internal key and `R` is the Merkle root.
pub fn from_key_and_tweak(
internal_key: UntweakedPublicKey,
pub fn from_key_and_tweak<K: Into<UntweakedPublicKey>>(
internal_key: K,
merkle_root: Option<TapNodeHash>,
) -> TapTweakHash {
let internal_key = internal_key.into();
let mut eng = sha256t::Hash::<TapTweakTag>::engine();
// always hash the key
eng.input(&internal_key.serialize());
@ -247,14 +249,15 @@ impl TaprootSpendInfo {
/// weights of satisfaction for that script.
///
/// See [`TaprootBuilder::with_huffman_tree`] for more detailed documentation.
pub fn with_huffman_tree<C, I>(
pub fn with_huffman_tree<C, I, K>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
internal_key: K,
script_weights: I,
) -> Result<Self, TaprootBuilderError>
where
I: IntoIterator<Item = (u32, ScriptBuf)>,
C: secp256k1::Verification,
K: Into<UntweakedPublicKey>,
{
let builder = TaprootBuilder::with_huffman_tree(script_weights)?;
Ok(builder.finalize(secp, internal_key).expect("Huffman tree is always complete"))
@ -271,11 +274,12 @@ impl TaprootSpendInfo {
///
/// Refer to BIP 341 footnote ('Why should the output key always have a Taproot commitment, even
/// if there is no script path?') for more details.
pub fn new_key_spend<C: secp256k1::Verification>(
pub fn new_key_spend<C: secp256k1::Verification, K: Into<UntweakedPublicKey>>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
internal_key: K,
merkle_root: Option<TapNodeHash>,
) -> Self {
let internal_key = internal_key.into();
let (output_key, parity) = internal_key.tap_tweak(secp, merkle_root);
Self {
internal_key,
@ -311,9 +315,9 @@ impl TaprootSpendInfo {
///
/// This is useful when you want to manually build a Taproot tree without using
/// [`TaprootBuilder`].
pub fn from_node_info<C: secp256k1::Verification>(
pub fn from_node_info<C: secp256k1::Verification, K: Into<UntweakedPublicKey>>(
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
internal_key: K,
node: NodeInfo,
) -> TaprootSpendInfo {
// Create as if it is a key spend path with the given Merkle root
@ -582,11 +586,12 @@ impl TaprootBuilder {
///
/// Returns the unmodified builder as Err if the builder is not finalizable.
/// See also [`TaprootBuilder::is_finalizable`]
pub fn finalize<C: secp256k1::Verification>(
pub fn finalize<C: secp256k1::Verification, K: Into<XOnlyPublicKey>>(
mut self,
secp: &Secp256k1<C>,
internal_key: UntweakedPublicKey,
internal_key: K,
) -> Result<TaprootSpendInfo, TaprootBuilder> {
let internal_key = internal_key.into();
match self.branch.len() {
0 => Ok(TaprootSpendInfo::new_key_spend(secp, internal_key, None)),
1 =>
@ -1523,7 +1528,7 @@ pub enum TaprootError {
/// Invalid control block size.
InvalidControlBlockSize(InvalidControlBlockSizeError),
/// Invalid Taproot internal key.
InvalidInternalKey(secp256k1::Error),
InvalidInternalKey(ParseXOnlyPublicKeyError),
/// Invalid control block hex
InvalidControlBlockHex(HexToBytesError),
}

View File

@ -11,9 +11,9 @@ use bitcoin::taproot::{LeafVersion, TaprootBuilder, TaprootSpendInfo};
use bitcoin::transaction::Version;
use bitcoin::{
absolute, script, Address, Network, OutPoint, PrivateKey, Psbt, ScriptBuf, Sequence,
Transaction, TxIn, TxOut, Witness,
Transaction, TxIn, TxOut, Witness, XOnlyPublicKey,
};
use secp256k1::{Keypair, Secp256k1, Signing, XOnlyPublicKey};
use secp256k1::{Keypair, Secp256k1, Signing};
use units::Amount;
#[test]
@ -146,7 +146,7 @@ fn psbt_sign_taproot() {
sig,
psbt_script_path_spend.inputs[0]
.tap_script_sigs
.get(&(x_only_pubkey, script2.clone().tapscript_leaf_hash()))
.get(&(x_only_pubkey.into(), script2.clone().tapscript_leaf_hash()))
.unwrap()
.signature
.to_string()
@ -176,13 +176,14 @@ fn create_basic_single_sig_script(secp: &Secp256k1<secp256k1::All>, sk: &str) ->
.into_script()
}
fn create_taproot_tree(
fn create_taproot_tree<K: Into<XOnlyPublicKey>>(
secp: &Secp256k1<secp256k1::All>,
script1: ScriptBuf,
script2: ScriptBuf,
script3: ScriptBuf,
internal_key: XOnlyPublicKey,
internal_key: K,
) -> TaprootSpendInfo {
let internal_key = internal_key.into();
let builder = TaprootBuilder::new();
let builder = builder.add_leaf(2, script1).unwrap();
let builder = builder.add_leaf(2, script2).unwrap();
@ -267,14 +268,15 @@ fn finalize_psbt_for_key_path_spend(mut psbt: Psbt) -> Psbt {
psbt
}
fn create_psbt_for_taproot_script_path_spend(
fn create_psbt_for_taproot_script_path_spend<K: Into<XOnlyPublicKey>>(
from_address: Address,
to_address: Address,
tree: TaprootSpendInfo,
x_only_pubkey_of_signing_key: XOnlyPublicKey,
x_only_pubkey_of_signing_key: K,
signing_key_path: &str,
use_script: ScriptBuf,
) -> Psbt {
let x_only_pubkey_of_signing_key = x_only_pubkey_of_signing_key.into();
let utxo_value = 6280;
let send_value = 6000;
let mfp = "73c5da0a";