Skip to content

Commit 888cd62

Browse files
committed
Add support for parsing Tapscript Miniscripts
1 parent 9a5384d commit 888cd62

File tree

5 files changed

+190
-63
lines changed

5 files changed

+190
-63
lines changed

src/lib.rs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,14 @@ impl MiniscriptKey for bitcoin::PublicKey {
159159
}
160160
}
161161

162+
impl MiniscriptKey for bitcoin::schnorr::PublicKey {
163+
type Hash = hash160::Hash;
164+
165+
fn to_pubkeyhash(&self) -> Self::Hash {
166+
hash160::Hash::hash(&self.serialize())
167+
}
168+
}
169+
162170
impl MiniscriptKey for String {
163171
type Hash = String;
164172

@@ -197,6 +205,25 @@ impl ToPublicKey for bitcoin::PublicKey {
197205
}
198206
}
199207

208+
impl ToPublicKey for bitcoin::schnorr::PublicKey {
209+
fn to_public_key(&self) -> bitcoin::PublicKey {
210+
// This code should never be used.
211+
// But is implemented for completeness
212+
let mut data: Vec<u8> = vec![0x02];
213+
data.extend(self.serialize().iter());
214+
bitcoin::PublicKey::from_slice(&data)
215+
.expect("Failed to construct 33 Publickey from 0x02 appended x-only key")
216+
}
217+
218+
fn to_x_only_pubkey(&self) -> bitcoin::schnorr::PublicKey {
219+
*self
220+
}
221+
222+
fn hash_to_hash160(hash: &hash160::Hash) -> hash160::Hash {
223+
*hash
224+
}
225+
}
226+
200227
/// Dummy key which de/serializes to the empty string; useful sometimes for testing
201228
#[derive(Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
202229
pub struct DummyKey;
@@ -443,7 +470,7 @@ pub enum Error {
443470
InvalidOpcode(opcodes::All),
444471
/// Some opcode occurred followed by `OP_VERIFY` when it had
445472
/// a `VERIFY` version that should have been used instead
446-
NonMinimalVerify(miniscript::lex::Token),
473+
NonMinimalVerify(String),
447474
/// Push was illegal in some context
448475
InvalidPush(Vec<u8>),
449476
/// rust-bitcoin script error
@@ -512,6 +539,8 @@ pub enum Error {
512539
ImpossibleSatisfaction,
513540
/// Bare descriptors don't have any addresses
514541
BareDescriptorAddr,
542+
/// PubKey invalid under current context
543+
PubKeyCtxError(miniscript::decode::KeyParseError, &'static str),
515544
}
516545

517546
#[doc(hidden)]
@@ -575,7 +604,7 @@ impl fmt::Display for Error {
575604
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
576605
match *self {
577606
Error::InvalidOpcode(op) => write!(f, "invalid opcode {}", op),
578-
Error::NonMinimalVerify(tok) => write!(f, "{} VERIFY", tok),
607+
Error::NonMinimalVerify(ref tok) => write!(f, "{} VERIFY", tok),
579608
Error::InvalidPush(ref push) => write!(f, "invalid push {:?}", push), // TODO hexify this
580609
Error::Script(ref e) => fmt::Display::fmt(e, f),
581610
Error::CmsTooManyKeys(n) => write!(f, "checkmultisig with {} keys", n),
@@ -629,6 +658,9 @@ impl fmt::Display for Error {
629658
Error::AnalysisError(ref e) => e.fmt(f),
630659
Error::ImpossibleSatisfaction => write!(f, "Impossible to satisfy Miniscript"),
631660
Error::BareDescriptorAddr => write!(f, "Bare descriptors don't have address"),
661+
Error::PubKeyCtxError(ref pk, ref ctx) => {
662+
write!(f, "Pubkey error: {} under {} scriptcontext", pk, ctx)
663+
}
632664
}
633665
}
634666
}

src/miniscript/context.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
use std::{fmt, hash};
1616

17+
use bitcoin;
1718
use bitcoin::blockdata::constants::MAX_BLOCK_WEIGHT;
1819
use miniscript::limits::{
1920
MAX_OPS_PER_SCRIPT, MAX_SCRIPTSIG_SIZE, MAX_SCRIPT_ELEMENT_SIZE, MAX_SCRIPT_SIZE,
@@ -23,6 +24,8 @@ use miniscript::types;
2324
use util::witness_to_scriptsig;
2425
use Error;
2526

27+
use super::decode::ParseableKey;
28+
2629
use {Miniscript, MiniscriptKey, Terminal};
2730

2831
/// Error for Script Context
@@ -129,7 +132,11 @@ impl fmt::Display for ScriptContextError {
129132
/// For example, disallowing uncompressed keys in Segwit context
130133
pub trait ScriptContext:
131134
fmt::Debug + Clone + Ord + PartialOrd + Eq + PartialEq + hash::Hash + private::Sealed
135+
where
136+
Self::Key: MiniscriptKey<Hash = bitcoin::hashes::hash160::Hash>,
132137
{
138+
/// The consensus key associated with the type. Must be a parseable key
139+
type Key: ParseableKey;
133140
/// Depending on ScriptContext, fragments can be malleable. For Example,
134141
/// under Legacy context, PkH is malleable because it is possible to
135142
/// estimate the cost of satisfaction because of compressed keys
@@ -276,6 +283,9 @@ pub trait ScriptContext:
276283
/// 34/66 for Bare/Legacy based on key compressedness
277284
/// 34 for Segwitv0, 33 for Tap
278285
fn pk_len<Pk: MiniscriptKey>(pk: &Pk) -> usize;
286+
287+
/// Local helper function to display error messages with context
288+
fn to_str() -> &'static str;
279289
}
280290

281291
/// Legacy ScriptContext
@@ -286,6 +296,7 @@ pub trait ScriptContext:
286296
pub enum Legacy {}
287297

288298
impl ScriptContext for Legacy {
299+
type Key = bitcoin::PublicKey;
289300
fn check_terminal_non_malleable<Pk: MiniscriptKey, Ctx: ScriptContext>(
290301
frag: &Terminal<Pk, Ctx>,
291302
) -> Result<(), ScriptContextError> {
@@ -358,13 +369,18 @@ impl ScriptContext for Legacy {
358369
34
359370
}
360371
}
372+
373+
fn to_str() -> &'static str {
374+
"Legacy/p2sh"
375+
}
361376
}
362377

363378
/// Segwitv0 ScriptContext
364379
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
365380
pub enum Segwitv0 {}
366381

367382
impl ScriptContext for Segwitv0 {
383+
type Key = bitcoin::PublicKey;
368384
fn check_terminal_non_malleable<Pk: MiniscriptKey, Ctx: ScriptContext>(
369385
_frag: &Terminal<Pk, Ctx>,
370386
) -> Result<(), ScriptContextError> {
@@ -451,13 +467,18 @@ impl ScriptContext for Segwitv0 {
451467
fn pk_len<Pk: MiniscriptKey>(_pk: &Pk) -> usize {
452468
34
453469
}
470+
471+
fn to_str() -> &'static str {
472+
"Segwitv0"
473+
}
454474
}
455475

456476
/// Tap ScriptContext
457477
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
458478
pub enum Tap {}
459479

460480
impl ScriptContext for Tap {
481+
type Key = bitcoin::schnorr::PublicKey;
461482
fn check_terminal_non_malleable<Pk: MiniscriptKey, Ctx: ScriptContext>(
462483
_frag: &Terminal<Pk, Ctx>,
463484
) -> Result<(), ScriptContextError> {
@@ -546,6 +567,10 @@ impl ScriptContext for Tap {
546567
fn pk_len<Pk: MiniscriptKey>(_pk: &Pk) -> usize {
547568
33
548569
}
570+
571+
fn to_str() -> &'static str {
572+
"TapscriptCtx"
573+
}
549574
}
550575

551576
/// Bare ScriptContext
@@ -556,6 +581,7 @@ impl ScriptContext for Tap {
556581
pub enum BareCtx {}
557582

558583
impl ScriptContext for BareCtx {
584+
type Key = bitcoin::PublicKey;
559585
fn check_terminal_non_malleable<Pk: MiniscriptKey, Ctx: ScriptContext>(
560586
_frag: &Terminal<Pk, Ctx>,
561587
) -> Result<(), ScriptContextError> {
@@ -615,6 +641,10 @@ impl ScriptContext for BareCtx {
615641
33
616642
}
617643
}
644+
645+
fn to_str() -> &'static str {
646+
"BareCtx"
647+
}
618648
}
619649

620650
/// "No Checks" Context
@@ -625,6 +655,8 @@ impl ScriptContext for BareCtx {
625655
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
626656
pub enum NoChecks {}
627657
impl ScriptContext for NoChecks {
658+
// todo: When adding support for interpreter, we need a enum with all supported keys here
659+
type Key = bitcoin::PublicKey;
628660
fn check_terminal_non_malleable<Pk: MiniscriptKey, Ctx: ScriptContext>(
629661
_frag: &Terminal<Pk, Ctx>,
630662
) -> Result<(), ScriptContextError> {
@@ -664,6 +696,11 @@ impl ScriptContext for NoChecks {
664696
fn pk_len<Pk: MiniscriptKey>(_pk: &Pk) -> usize {
665697
panic!("Tried to compute a pk len bound on a no-checks miniscript")
666698
}
699+
700+
fn to_str() -> &'static str {
701+
// Internally used code
702+
"NochecksCtx"
703+
}
667704
}
668705

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

0 commit comments

Comments
 (0)