Skip to content

Commit 4625419

Browse files
committed
Add BitcoinSchnorrSig type
1 parent 51627a0 commit 4625419

File tree

2 files changed

+153
-3
lines changed

2 files changed

+153
-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: 144 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use std::sync::Arc;
2323
use std::{cmp, i64, mem};
2424

2525
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
26+
use bitcoin::util::sighash;
2627
use bitcoin::{self, secp256k1};
2728
use {MiniscriptKey, ToPublicKey};
2829

@@ -34,14 +35,17 @@ use Miniscript;
3435
use ScriptContext;
3536
use Terminal;
3637

37-
/// Type for a signature/hashtype pair
38+
type LegacySigHashType = bitcoin::SigHashType;
39+
type SchnorrSigHashType = sighash::SigHashType;
40+
/// Type for a EC signature/hashtype pair
3841
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
3942
pub struct BitcoinECSig {
4043
/// The underlying signature
4144
pub sig: secp256k1::Signature,
4245
/// The associated hash type
43-
pub hash_ty: bitcoin::SigHashType,
46+
pub hash_ty: LegacySigHashType,
4447
}
48+
4549
/// Type alias for 32 byte Preimage.
4650
pub type Preimage32 = [u8; 32];
4751

@@ -50,7 +54,9 @@ impl BitcoinECSig {
5054
/// Useful for downstream when implementing Satisfier.
5155
/// Returns underlying secp if the Signature is not of correct format
5256
pub fn from_rawsig(rawsig: &[u8]) -> Result<BitcoinECSig, ::interpreter::Error> {
53-
let (flag, sig) = rawsig.split_last().unwrap();
57+
let (flag, sig) = rawsig
58+
.split_last()
59+
.ok_or(::interpreter::Error::NonStandardSigHash(Vec::from(rawsig)))?;
5460
let flag = bitcoin::SigHashType::from_u32_standard(*flag as u32).map_err(|_| {
5561
::interpreter::Error::NonStandardSigHash([sig, &[*flag]].concat().to_vec())
5662
})?;
@@ -69,6 +75,53 @@ impl BitcoinECSig {
6975
}
7076
}
7177

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

135+
/// Given a public key, look up an schnorr signature with that key
136+
fn lookup_schnorr_sig(&self, _: &Pk) -> Option<BitcoinSchnorrSig> {
137+
None
138+
}
139+
82140
/// Given a `Pkh`, lookup corresponding `Pk`
83141
fn lookup_pkh_pk(&self, _: &Pk::Hash) -> Option<Pk> {
84142
None
@@ -92,6 +150,17 @@ pub trait Satisfier<Pk: MiniscriptKey + ToPublicKey> {
92150
None
93151
}
94152

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

244+
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for HashMap<Pk, BitcoinSchnorrSig> {
245+
fn lookup_schnorr_sig(&self, key: &Pk) -> Option<BitcoinSchnorrSig> {
246+
self.get(key).map(|x| *x)
247+
}
248+
}
249+
175250
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for HashMap<Pk::Hash, (Pk, BitcoinECSig)>
176251
where
177252
Pk: MiniscriptKey + ToPublicKey,
@@ -190,11 +265,36 @@ where
190265
}
191266
}
192267

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

294+
fn lookup_schnorr_sig(&self, p: &Pk) -> Option<BitcoinSchnorrSig> {
295+
(**self).lookup_schnorr_sig(p)
296+
}
297+
198298
fn lookup_pkh_pk(&self, pkh: &Pk::Hash) -> Option<Pk> {
199299
(**self).lookup_pkh_pk(pkh)
200300
}
@@ -203,6 +303,13 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
203303
(**self).lookup_pkh_ec_sig(pkh)
204304
}
205305

306+
fn lookup_pkh_schnorr_sig(
307+
&self,
308+
pkh: &Pk::Hash,
309+
) -> Option<(bitcoin::PublicKey, BitcoinSchnorrSig)> {
310+
(**self).lookup_pkh_schnorr_sig(pkh)
311+
}
312+
206313
fn lookup_sha256(&self, h: sha256::Hash) -> Option<Preimage32> {
207314
(**self).lookup_sha256(h)
208315
}
@@ -233,6 +340,10 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
233340
(**self).lookup_ec_sig(p)
234341
}
235342

343+
fn lookup_schnorr_sig(&self, p: &Pk) -> Option<BitcoinSchnorrSig> {
344+
(**self).lookup_schnorr_sig(p)
345+
}
346+
236347
fn lookup_pkh_pk(&self, pkh: &Pk::Hash) -> Option<Pk> {
237348
(**self).lookup_pkh_pk(pkh)
238349
}
@@ -241,6 +352,13 @@ impl<'a, Pk: MiniscriptKey + ToPublicKey, S: Satisfier<Pk>> Satisfier<Pk> for &'
241352
(**self).lookup_pkh_ec_sig(pkh)
242353
}
243354

355+
fn lookup_pkh_schnorr_sig(
356+
&self,
357+
pkh: &Pk::Hash,
358+
) -> Option<(bitcoin::PublicKey, BitcoinSchnorrSig)> {
359+
(**self).lookup_pkh_schnorr_sig(pkh)
360+
}
361+
244362
fn lookup_sha256(&self, h: sha256::Hash) -> Option<Preimage32> {
245363
(**self).lookup_sha256(h)
246364
}
@@ -284,6 +402,16 @@ macro_rules! impl_tuple_satisfier {
284402
None
285403
}
286404

405+
fn lookup_schnorr_sig(&self, key: &Pk) -> Option<BitcoinSchnorrSig> {
406+
let &($(ref $ty,)*) = self;
407+
$(
408+
if let Some(result) = $ty.lookup_schnorr_sig(key) {
409+
return Some(result);
410+
}
411+
)*
412+
None
413+
}
414+
287415
fn lookup_pkh_ec_sig(
288416
&self,
289417
key_hash: &Pk::Hash,
@@ -297,6 +425,19 @@ macro_rules! impl_tuple_satisfier {
297425
None
298426
}
299427

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

0 commit comments

Comments
 (0)