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: ACKc11772a768
Tree-SHA512: c8da3486e7ffcab6c24cc08f9b2f964dd9158449ef2bd720e54d56176bc7027052314ea23cac3f673d217fa785238ea8a9b5323ba57f02199f20e56df5893965
This commit is contained in:
commit
7d4b40dfd4
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
},
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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),
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
|
Loading…
Reference in New Issue