Skip to content

Commit 6606a0d

Browse files
committed
Merge #159: Tap tweak
7b3f081 Add From implementations (Christian Lewe) d58610e Add from_keypair (Christian Lewe) 4eed656 Add TweakedKeyPair methods (Christian Lewe) 19ff852 Add TweakedKeyPair (Christian Lewe) 063a8f5 Update TapTweak (Christian Lewe) Pull request description: Updates `TapTweak` to [latest rust-bitcoin](https://docs.rs/bitcoin/latest/bitcoin/key/trait.TapTweak.html) and adds `TweakedKeyPair`. ACKs for top commit: apoelstra: ACK 7b3f081 Tree-SHA512: 6a842533ce9a4483b3618b8a395b223f6943cd3ba79588805d65ad4ddbd04fac17f03a1eadaaecef267035d8a6dac8ac075e4c2ecee8e27134929c6073728ef5
2 parents 40e3035 + 7b3f081 commit 6606a0d

2 files changed

Lines changed: 138 additions & 22 deletions

File tree

src/schnorr.rs

Lines changed: 132 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@
1919
2020
use std::fmt;
2121

22-
use secp256k1_zkp::Scalar;
23-
pub use secp256k1_zkp::{XOnlyPublicKey, KeyPair};
24-
use secp256k1_zkp::{self, Secp256k1, Verification, constants::SCHNORR_SIGNATURE_SIZE};
25-
use crate::hashes::{Hash, HashEngine};
2622
use crate::taproot::{TapBranchHash, TapTweakHash};
2723
use crate::SchnorrSigHashType;
24+
use secp256k1_zkp::{self, constants::SCHNORR_SIGNATURE_SIZE, Secp256k1, Verification};
25+
pub use secp256k1_zkp::{KeyPair, XOnlyPublicKey};
2826

2927
/// Untweaked Schnorr public key
3028
pub type UntweakedPublicKey = XOnlyPublicKey;
@@ -33,38 +31,70 @@ pub type UntweakedPublicKey = XOnlyPublicKey;
3331
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
3432
pub struct TweakedPublicKey(XOnlyPublicKey);
3533

36-
/// A trait for tweaking Schnorr public keys
34+
/// Untweaked Schnorr key pair
35+
pub type UntweakedKeyPair = KeyPair;
36+
37+
/// Tweaked Schnorr key pair
38+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
39+
pub struct TweakedKeyPair(KeyPair);
40+
41+
/// A trait for tweaking Schnorr key types (x-only public keys and key pairs).
3742
pub trait TapTweak {
38-
/// Tweaks an untweaked public key given an untweaked key and optional script tree merkle root.
43+
/// Tweaked key type with optional auxiliary information
44+
type TweakedAux;
45+
/// Tweaked key type
46+
type TweakedKey;
47+
48+
/// Tweaks an untweaked key with corresponding public key value and optional script tree merkle
49+
/// root. For the [`KeyPair`] type this also tweaks the private key in the pair.
3950
///
4051
/// This is done by using the equation Q = P + H(P|c)G, where
41-
/// * Q is the tweaked key
42-
/// * P is the internal key
52+
/// * Q is the tweaked public key
53+
/// * P is the internal public key
4354
/// * H is the hash function
4455
/// * c is the commitment data
4556
/// * G is the generator point
46-
fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapBranchHash>) -> (TweakedPublicKey, secp256k1_zkp::Parity);
57+
///
58+
/// # Returns
59+
/// The tweaked key and its parity.
60+
fn tap_tweak<C: Verification>(
61+
self,
62+
secp: &Secp256k1<C>,
63+
merkle_root: Option<TapBranchHash>,
64+
) -> Self::TweakedAux;
4765

48-
/// Directly convert an UntweakedPublicKey to a TweakedPublicKey
66+
/// Directly converts to a `TweakedKey`
4967
///
5068
/// This method is dangerous and can lead to loss of funds if used incorrectly.
5169
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
52-
fn dangerous_assume_tweaked(self) -> TweakedPublicKey;
70+
fn dangerous_assume_tweaked(self) -> Self::TweakedKey;
5371
}
5472

5573
impl TapTweak for UntweakedPublicKey {
56-
fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapBranchHash>) -> (TweakedPublicKey, secp256k1_zkp::Parity) {
57-
// Compute the tweak
58-
let mut engine = TapTweakHash::engine();
59-
engine.input(&self.serialize());
60-
merkle_root.map(|hash| engine.input(&hash));
61-
let tweak_value: [u8; 32] = TapTweakHash::from_engine(engine).into_inner();
62-
let tweak_value = Scalar::from_be_bytes(tweak_value).expect("hash value greater than curve order");
63-
64-
//Tweak the internal key by the tweak value
65-
let (output_key, parity) = self.clone().add_tweak(secp, &tweak_value).expect("Tap tweak failed");
66-
debug_assert!(self.tweak_add_check(&secp, &output_key, parity, tweak_value));
74+
type TweakedAux = (TweakedPublicKey, secp256k1_zkp::Parity);
75+
type TweakedKey = TweakedPublicKey;
76+
77+
/// Tweaks an untweaked public key with corresponding public key value and optional script tree
78+
/// merkle root.
79+
///
80+
/// This is done by using the equation Q = P + H(P|c)G, where
81+
/// * Q is the tweaked public key
82+
/// * P is the internal public key
83+
/// * H is the hash function
84+
/// * c is the commitment data
85+
/// * G is the generator point
86+
///
87+
/// # Returns
88+
/// The tweaked key and its parity.
89+
fn tap_tweak<C: Verification>(
90+
self,
91+
secp: &Secp256k1<C>,
92+
merkle_root: Option<TapBranchHash>,
93+
) -> (TweakedPublicKey, secp256k1_zkp::Parity) {
94+
let tweak = TapTweakHash::from_key_and_tweak(self, merkle_root).to_scalar();
95+
let (output_key, parity) = self.add_tweak(secp, &tweak).expect("Tap tweak failed");
6796

97+
debug_assert!(self.tweak_add_check(secp, &output_key, parity, tweak));
6898
(TweakedPublicKey(output_key), parity)
6999
}
70100

@@ -74,8 +104,42 @@ impl TapTweak for UntweakedPublicKey {
74104
}
75105
}
76106

107+
impl TapTweak for UntweakedKeyPair {
108+
type TweakedAux = TweakedKeyPair;
109+
type TweakedKey = TweakedKeyPair;
110+
111+
/// Tweaks private and public keys within an untweaked [`KeyPair`] with corresponding public key
112+
/// value and optional script tree merkle root.
113+
///
114+
/// This is done by tweaking private key within the pair using the equation q = p + H(P|c), where
115+
/// * q is the tweaked private key
116+
/// * p is the internal private key
117+
/// * H is the hash function
118+
/// * c is the commitment data
119+
/// The public key is generated from a private key by multiplying with generator point, Q = qG.
120+
///
121+
/// # Returns
122+
/// The tweaked key and its parity.
123+
fn tap_tweak<C: Verification>(self, secp: &Secp256k1<C>, merkle_root: Option<TapBranchHash>) -> TweakedKeyPair {
124+
let (pubkey, _parity) = XOnlyPublicKey::from_keypair(&self);
125+
let tweak = TapTweakHash::from_key_and_tweak(pubkey, merkle_root).to_scalar();
126+
let tweaked = self.add_xonly_tweak(secp, &tweak).expect("Tap tweak failed");
127+
TweakedKeyPair(tweaked)
128+
}
129+
130+
fn dangerous_assume_tweaked(self) -> TweakedKeyPair {
131+
TweakedKeyPair(self)
132+
}
133+
}
77134

78135
impl TweakedPublicKey {
136+
/// Returns the [`TweakedPublicKey`] for `keypair`.
137+
#[inline]
138+
pub fn from_keypair(keypair: TweakedKeyPair) -> Self {
139+
let (xonly, _parity) = keypair.0.x_only_public_key();
140+
TweakedPublicKey(xonly)
141+
}
142+
79143
/// Create a new [TweakedPublicKey] from a [PublicKey]. No tweak is applied.
80144
pub fn new(key: XOnlyPublicKey) -> TweakedPublicKey {
81145
TweakedPublicKey(key)
@@ -92,6 +156,52 @@ impl TweakedPublicKey {
92156
}
93157
}
94158

159+
impl TweakedKeyPair {
160+
/// Creates a new [`TweakedKeyPair`] from a [`KeyPair`]. No tweak is applied, consider
161+
/// calling `tap_tweak` on an [`UntweakedKeyPair`] instead of using this constructor.
162+
///
163+
/// This method is dangerous and can lead to loss of funds if used incorrectly.
164+
/// Specifically, in multi-party protocols a peer can provide a value that allows them to steal.
165+
#[inline]
166+
pub fn dangerous_assume_tweaked(pair: KeyPair) -> TweakedKeyPair {
167+
TweakedKeyPair(pair)
168+
}
169+
170+
/// Returns the underlying key pair.
171+
#[inline]
172+
pub fn to_inner(self) -> KeyPair {
173+
self.0
174+
}
175+
176+
/// Returns the [`TweakedPublicKey`] and its [`Parity`] for this [`TweakedKeyPair`].
177+
#[inline]
178+
pub fn public_parts(&self) -> (TweakedPublicKey, secp256k1_zkp::Parity) {
179+
let (xonly, parity) = self.0.x_only_public_key();
180+
(TweakedPublicKey(xonly), parity)
181+
}
182+
}
183+
184+
impl From<TweakedPublicKey> for XOnlyPublicKey {
185+
#[inline]
186+
fn from(pair: TweakedPublicKey) -> Self {
187+
pair.0
188+
}
189+
}
190+
191+
impl From<TweakedKeyPair> for KeyPair {
192+
#[inline]
193+
fn from(pair: TweakedKeyPair) -> Self {
194+
pair.0
195+
}
196+
}
197+
198+
impl From<TweakedKeyPair> for TweakedPublicKey {
199+
#[inline]
200+
fn from(pair: TweakedKeyPair) -> Self {
201+
TweakedPublicKey::from_keypair(pair)
202+
}
203+
}
204+
95205
/// A BIP340-341 serialized schnorr signature with the corresponding hash type.
96206
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
97207
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(crate = "actual_serde"))]

src/taproot.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,12 @@ impl TapTweakHash {
107107
}
108108
TapTweakHash::from_engine(eng)
109109
}
110+
111+
/// Converts a `TapTweakHash` into a `Scalar` ready for use with key tweaking API.
112+
pub fn to_scalar(self) -> Scalar {
113+
// This is statistically extremely unlikely to panic.
114+
Scalar::from_be_bytes(self.into_inner()).expect("hash value greater than curve order")
115+
}
110116
}
111117

112118
impl TapLeafHash {

0 commit comments

Comments
 (0)