Skip to content

Commit 3d3bc53

Browse files
committed
Add BitcoinSchnorrSig type
1 parent 8aed9dd commit 3d3bc53

File tree

2 files changed

+152
-3
lines changed

2 files changed

+152
-3
lines changed

src/interpreter/error.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ pub enum Error {
3737
IncorrectWScriptHash,
3838
/// MultiSig missing at least `1` witness elements out of `k + 1` required
3939
InsufficientSignaturesMultiSig,
40+
/// Invalid Sighash type
41+
InvalidSchnorrSigHashType(Vec<u8>),
4042
/// Signature failed to verify
4143
InvalidSignature(bitcoin::PublicKey),
4244
/// Last byte of this signature isn't a standard sighash type
@@ -138,6 +140,13 @@ impl fmt::Display for Error {
138140
}
139141
Error::IncorrectWScriptHash => f.write_str("witness script did not match scriptpubkey"),
140142
Error::InsufficientSignaturesMultiSig => f.write_str("Insufficient signatures for CMS"),
143+
Error::InvalidSchnorrSigHashType(ref sig) => {
144+
write!(
145+
f,
146+
"Invalid sighash type for schnorr signature '{}'",
147+
sig.to_hex()
148+
)
149+
}
141150
Error::InvalidSignature(pk) => write!(f, "bad signature with pk {}", pk),
142151
Error::NonStandardSigHash(ref sig) => {
143152
write!(

src/miniscript/satisfy.rs

Lines changed: 143 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,17 @@ use Miniscript;
3434
use ScriptContext;
3535
use Terminal;
3636

37-
/// Type for a signature/hashtype pair
37+
type LegacySigHashType = bitcoin::SigHashType;
38+
type SchnorrSigHashType = bitcoin::util::sighash::SigHashType;
39+
/// Type for a EC signature/hashtype pair
3840
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3941
pub struct BitcoinECSig {
4042
/// The underlying signature
4143
pub sig: secp256k1::Signature,
4244
/// The associated hash type
43-
pub hash_ty: bitcoin::SigHashType,
45+
pub hash_ty: LegacySigHashType,
4446
}
47+
4548
/// Type alias for 32 byte Preimage.
4649
pub type Preimage32 = [u8; 32];
4750

@@ -50,7 +53,9 @@ impl BitcoinECSig {
5053
/// Useful for downstream when implementing Satisfier.
5154
/// Returns underlying secp if the Signature is not of correct format
5255
pub fn from_rawsig(rawsig: &[u8]) -> Result<BitcoinECSig, ::interpreter::Error> {
53-
let (flag, sig) = rawsig.split_last().unwrap();
56+
let (flag, sig) = rawsig
57+
.split_last()
58+
.ok_or(::interpreter::Error::NonStandardSigHash(Vec::from(rawsig)))?;
5459
let flag = bitcoin::SigHashType::from_u32_standard(*flag as u32).map_err(|_| {
5560
::interpreter::Error::NonStandardSigHash([sig, &[*flag]].concat().to_vec())
5661
})?;
@@ -69,6 +74,53 @@ impl BitcoinECSig {
6974
}
7075
}
7176

77+
impl BitcoinSchnorrSig {
78+
/// Helper function to create BitcoinSchnorrSig from raw sig bytes
79+
/// Useful for downstream when implementing Satisfier.
80+
/// Returns underlying secp if the Signature is not of correct format
81+
pub fn from_rawsig(rawsig: &[u8]) -> Result<BitcoinSchnorrSig, ::interpreter::Error> {
82+
let (flag, sig) = if rawsig.len() == 65 {
83+
let (flag, sig) = rawsig.split_last().unwrap();
84+
// Workaround to parse sighash type.
85+
let legacy_flag = LegacySigHashType::from_u32_standard(*flag as u32).map_err(|_| {
86+
::interpreter::Error::InvalidSchnorrSigHashType([sig, &[*flag]].concat().to_vec())
87+
})?;
88+
let schnorr_sighash_ty = SchnorrSigHashType::from(legacy_flag);
89+
let schnorr_sig = secp256k1::schnorrsig::Signature::from_slice(sig)?;
90+
(schnorr_sighash_ty, schnorr_sig)
91+
} else {
92+
// Other lengths would be correctly handled by secp
93+
let schnorr_sig = secp256k1::schnorrsig::Signature::from_slice(rawsig)?;
94+
(bitcoin::util::sighash::SigHashType::Default, schnorr_sig)
95+
};
96+
Ok(BitcoinSchnorrSig {
97+
sig: sig,
98+
hash_ty: flag,
99+
})
100+
}
101+
102+
/// Serialize to vec of bytes
103+
pub fn serialize(&self) -> Vec<u8> {
104+
let mut ret = self.sig.as_ref().to_vec();
105+
if let bitcoin::util::sighash::SigHashType::Default = self.hash_ty {
106+
// If the type is default, the sig is explicit 64 bytes
107+
// nothing to push for sighash type
108+
} else {
109+
ret.push(self.hash_ty as u8);
110+
}
111+
ret
112+
}
113+
}
114+
115+
/// Type for a schnorr signature/hashtype pair
116+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
117+
pub struct BitcoinSchnorrSig {
118+
/// The underlying schnorr signature
119+
pub sig: secp256k1::schnorrsig::Signature,
120+
/// The associated hash type
121+
pub hash_ty: SchnorrSigHashType,
122+
}
123+
72124
/// Trait describing a lookup table for signatures, hash preimages, etc.
73125
/// Every method has a default implementation that simply returns `None`
74126
/// on every query. Users are expected to override the methods that they
@@ -79,6 +131,11 @@ pub trait Satisfier<Pk: MiniscriptKey + ToPublicKey> {
79131
None
80132
}
81133

134+
/// Given a public key, look up an schnorr signature with that key
135+
fn lookup_schnorr_sig(&self, _: &Pk) -> Option<BitcoinSchnorrSig> {
136+
None
137+
}
138+
82139
/// Given a `Pkh`, lookup corresponding `Pk`
83140
fn lookup_pkh_pk(&self, _: &Pk::Hash) -> Option<Pk> {
84141
None
@@ -92,6 +149,17 @@ pub trait Satisfier<Pk: MiniscriptKey + ToPublicKey> {
92149
None
93150
}
94151

152+
/// Given a keyhash, look up the schnorr signature and the associated key
153+
/// Even if signatures for public key Hashes are not available, the users
154+
/// can use this map to provide pkh -> pk mapping which can be useful
155+
/// for dissatisfying pkh.
156+
fn lookup_pkh_schnorr_sig(
157+
&self,
158+
_: &Pk::Hash,
159+
) -> Option<(bitcoin::PublicKey, BitcoinSchnorrSig)> {
160+
None
161+
}
162+
95163
/// Given a SHA256 hash, look up its preimage
96164
fn lookup_sha256(&self, _: sha256::Hash) -> Option<Preimage32> {
97165
None
@@ -172,6 +240,12 @@ impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for HashMap<Pk, BitcoinECSig
172240
}
173241
}
174242

243+
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for HashMap<Pk, BitcoinSchnorrSig> {
244+
fn lookup_schnorr_sig(&self, key: &Pk) -> Option<BitcoinSchnorrSig> {
245+
self.get(key).map(|x| *x)
246+
}
247+
}
248+
175249
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for HashMap<Pk::Hash, (Pk, BitcoinECSig)>
176250
where
177251
Pk: MiniscriptKey + ToPublicKey,
@@ -190,11 +264,36 @@ where
190264
}
191265
}
192266

267+
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for HashMap<Pk::Hash, (Pk, BitcoinSchnorrSig)>
268+
where
269+
Pk: MiniscriptKey + ToPublicKey,
270+
{
271+
fn lookup_schnorr_sig(&self, key: &Pk) -> Option<BitcoinSchnorrSig> {
272+
self.get(&key.to_pubkeyhash()).map(|x| x.1)
273+
}
274+
275+
fn lookup_pkh_pk(&self, pk_hash: &Pk::Hash) -> Option<Pk> {
276+
self.get(pk_hash).map(|x| x.0.clone())
277+
}
278+
279+
fn lookup_pkh_schnorr_sig(
280+
&self,
281+
pk_hash: &Pk::Hash,
282+
) -> Option<(bitcoin::PublicKey, BitcoinSchnorrSig)> {
283+
self.get(pk_hash)
284+
.map(|&(ref pk, sig)| (pk.to_public_key(), sig))
285+
}
286+
}
287+
193288
impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'a S {
194289
fn lookup_ec_sig(&self, p: &Pk) -> Option<BitcoinECSig> {
195290
(**self).lookup_ec_sig(p)
196291
}
197292

293+
fn lookup_schnorr_sig(&self, p: &Pk) -> Option<BitcoinSchnorrSig> {
294+
(**self).lookup_schnorr_sig(p)
295+
}
296+
198297
fn lookup_pkh_pk(&self, pkh: &Pk::Hash) -> Option<Pk> {
199298
(**self).lookup_pkh_pk(pkh)
200299
}
@@ -203,6 +302,13 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
203302
(**self).lookup_pkh_ec_sig(pkh)
204303
}
205304

305+
fn lookup_pkh_schnorr_sig(
306+
&self,
307+
pkh: &Pk::Hash,
308+
) -> Option<(bitcoin::PublicKey, BitcoinSchnorrSig)> {
309+
(**self).lookup_pkh_schnorr_sig(pkh)
310+
}
311+
206312
fn lookup_sha256(&self, h: sha256::Hash) -> Option<Preimage32> {
207313
(**self).lookup_sha256(h)
208314
}
@@ -233,6 +339,10 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
233339
(**self).lookup_ec_sig(p)
234340
}
235341

342+
fn lookup_schnorr_sig(&self, p: &Pk) -> Option<BitcoinSchnorrSig> {
343+
(**self).lookup_schnorr_sig(p)
344+
}
345+
236346
fn lookup_pkh_pk(&self, pkh: &Pk::Hash) -> Option<Pk> {
237347
(**self).lookup_pkh_pk(pkh)
238348
}
@@ -241,6 +351,13 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
241351
(**self).lookup_pkh_ec_sig(pkh)
242352
}
243353

354+
fn lookup_pkh_schnorr_sig(
355+
&self,
356+
pkh: &Pk::Hash,
357+
) -> Option<(bitcoin::PublicKey, BitcoinSchnorrSig)> {
358+
(**self).lookup_pkh_schnorr_sig(pkh)
359+
}
360+
244361
fn lookup_sha256(&self, h: sha256::Hash) -> Option<Preimage32> {
245362
(**self).lookup_sha256(h)
246363
}
@@ -284,6 +401,16 @@ macro_rules! impl_tuple_satisfier {
284401
None
285402
}
286403

404+
fn lookup_schnorr_sig(&self, key: &Pk) -> Option<BitcoinSchnorrSig> {
405+
let &($(ref $ty,)*) = self;
406+
$(
407+
if let Some(result) = $ty.lookup_schnorr_sig(key) {
408+
return Some(result);
409+
}
410+
)*
411+
None
412+
}
413+
287414
fn lookup_pkh_ec_sig(
288415
&self,
289416
key_hash: &Pk::Hash,
@@ -297,6 +424,19 @@ macro_rules! impl_tuple_satisfier {
297424
None
298425
}
299426

427+
fn lookup_pkh_schnorr_sig(
428+
&self,
429+
key_hash: &Pk::Hash,
430+
) -> Option<(bitcoin::PublicKey, BitcoinSchnorrSig)> {
431+
let &($(ref $ty,)*) = self;
432+
$(
433+
if let Some(result) = $ty.lookup_pkh_schnorr_sig(key_hash) {
434+
return Some(result);
435+
}
436+
)*
437+
None
438+
}
439+
300440
fn lookup_pkh_pk(
301441
&self,
302442
key_hash: &Pk::Hash,

0 commit comments

Comments
 (0)