Skip to content

Commit 36eb009

Browse files
committed
Add support for parsing Tapscript Miniscripts
1 parent d984192 commit 36eb009

File tree

4 files changed

+395
-334
lines changed

4 files changed

+395
-334
lines changed

src/lib.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,14 @@ impl MiniscriptKey for bitcoin::PublicKey {
172172
}
173173
}
174174

175+
impl MiniscriptKey for bitcoin::schnorr::PublicKey {
176+
type Hash = hash160::Hash;
177+
178+
fn to_pubkeyhash(&self) -> Self::Hash {
179+
hash160::Hash::hash(&self.serialize())
180+
}
181+
}
182+
175183
impl MiniscriptKey for String {
176184
type Hash = String;
177185

@@ -222,6 +230,20 @@ impl ToPublicKey for bitcoin::PublicKey {
222230
}
223231
}
224232

233+
impl ToPublicKey for bitcoin::schnorr::PublicKey {
234+
fn to_public_key(&self) -> bitcoin::PublicKey {
235+
// This code should never be used.
236+
// But is implemented for completeness
237+
let mut data: Vec<u8> = vec![0x02];
238+
data.extend(self.serialize().iter());
239+
bitcoin::PublicKey::from_slice(&data).expect("Cannot fail")
240+
}
241+
242+
fn hash_to_hash160(hash: &hash160::Hash) -> hash160::Hash {
243+
*hash
244+
}
245+
}
246+
225247
/// Dummy key which de/serializes to the empty string; useful sometimes for testing
226248
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
227249
pub struct DummyKey;
@@ -468,7 +490,7 @@ pub enum Error {
468490
InvalidOpcode(opcodes::All),
469491
/// Some opcode occurred followed by `OP_VERIFY` when it had
470492
/// a `VERIFY` version that should have been used instead
471-
NonMinimalVerify(miniscript::lex::Token),
493+
NonMinimalVerify(String),
472494
/// Push was illegal in some context
473495
InvalidPush(Vec<u8>),
474496
/// rust-bitcoin script error
@@ -541,6 +563,8 @@ pub enum Error {
541563
ImpossibleSatisfaction,
542564
/// Bare descriptors don't have any addresses
543565
BareDescriptorAddr,
566+
/// PubKey invalid under current context
567+
PubKeyCtxError(String, String),
544568
}
545569

546570
#[doc(hidden)]
@@ -604,7 +628,7 @@ impl fmt::Display for Error {
604628
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
605629
match *self {
606630
Error::InvalidOpcode(op) => write!(f, "invalid opcode {}", op),
607-
Error::NonMinimalVerify(tok) => write!(f, "{} VERIFY", tok),
631+
Error::NonMinimalVerify(ref tok) => write!(f, "{} VERIFY", tok),
608632
Error::InvalidPush(ref push) => write!(f, "invalid push {:?}", push), // TODO hexify this
609633
Error::Script(ref e) => fmt::Display::fmt(e, f),
610634
Error::CmsTooManyKeys(n) => write!(f, "checkmultisig with {} keys", n),
@@ -662,6 +686,9 @@ impl fmt::Display for Error {
662686
Error::AnalysisError(ref e) => e.fmt(f),
663687
Error::ImpossibleSatisfaction => write!(f, "Impossible to satisfy Miniscript"),
664688
Error::BareDescriptorAddr => write!(f, "Bare descriptors don't have address"),
689+
Error::PubKeyCtxError(ref pk, ref ctx) => {
690+
write!(f, "Pubkey error: {} under {} scriptcontext", pk, ctx)
691+
}
665692
}
666693
}
667694
}

src/miniscript/context.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,9 @@ pub trait ScriptContext:
271271
fn is_tapctx() -> bool {
272272
return false;
273273
}
274+
275+
/// Local helper function to display error messages with context
276+
fn to_string() -> String;
274277
}
275278

276279
/// Legacy ScriptContext
@@ -345,6 +348,10 @@ impl ScriptContext for Legacy {
345348
// The scriptSig cost is the second element of the tuple
346349
ms.ext.max_sat_size.map(|x| x.1)
347350
}
351+
352+
fn to_string() -> String {
353+
String::from("Legacy/p2sh")
354+
}
348355
}
349356

350357
/// Segwitv0 ScriptContext
@@ -434,6 +441,10 @@ impl ScriptContext for Segwitv0 {
434441
// The witness stack cost is the first element of the tuple
435442
ms.ext.max_sat_size.map(|x| x.0)
436443
}
444+
445+
fn to_string() -> String {
446+
String::from("Segwitv0")
447+
}
437448
}
438449

439450
/// Tap ScriptContext
@@ -525,6 +536,10 @@ impl ScriptContext for Tap {
525536
fn is_tapctx() -> bool {
526537
true
527538
}
539+
540+
fn to_string() -> String {
541+
String::from("TapscriptCtx")
542+
}
528543
}
529544

530545
/// Bare ScriptContext
@@ -586,6 +601,10 @@ impl ScriptContext for BareCtx {
586601
// The witness stack cost is the first element of the tuple
587602
ms.ext.max_sat_size.map(|x| x.1)
588603
}
604+
605+
fn to_string() -> String {
606+
String::from("BareCtx")
607+
}
589608
}
590609

591610
/// "No Checks" Context
@@ -631,6 +650,11 @@ impl ScriptContext for NoChecks {
631650
) -> Option<usize> {
632651
panic!("Tried to compute a satisfaction size bound on a no-checks miniscript")
633652
}
653+
654+
fn to_string() -> String {
655+
// Internally used code
656+
String::from("NochecksCtx")
657+
}
634658
}
635659

636660
/// Private Mod to prevent downstream from implementing this public trait

0 commit comments

Comments
 (0)