Skip to content

Commit b65ed15

Browse files
committed
Add ToNoChecks trait
Rename TaggedHash160 -> TypedHash160
1 parent a9bb012 commit b65ed15

File tree

3 files changed

+54
-43
lines changed

3 files changed

+54
-43
lines changed

src/interpreter/inner.rs

Lines changed: 38 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use bitcoin::util::taproot::{ControlBlock, TAPROOT_ANNEX_PREFIX};
2020

2121
use {BareCtx, Legacy, Segwitv0, Tap};
2222

23-
use super::{stack, BitcoinKey, Error, Stack, TaggedHash160};
23+
use super::{stack, BitcoinKey, Error, Stack, TypedHash160};
2424
use miniscript::context::{NoChecks, ScriptContext};
2525
use {Miniscript, MiniscriptKey};
2626

@@ -186,7 +186,7 @@ pub(super) fn from_txdata<'txin>(
186186
Some(elem) => {
187187
let miniscript = script_from_stackelem::<Segwitv0>(&elem)?;
188188
let script = miniscript.encode();
189-
let miniscript = pre_taproot_to_no_checks(&miniscript);
189+
let miniscript = miniscript.to_no_checks_ms();
190190
let scripthash = sha256::Hash::hash(&script[..]);
191191
if *spk == bitcoin::Script::new_v0_p2wsh(&scripthash.into()) {
192192
Ok((
@@ -230,7 +230,7 @@ pub(super) fn from_txdata<'txin>(
230230
let ctrl_blk = ControlBlock::from_slice(ctrl_blk)
231231
.map_err(|e| Error::ControlBlockParse(e))?;
232232
let tap_script = script_from_stackelem::<Tap>(&tap_script)?;
233-
let ms = taproot_to_no_checks(&tap_script);
233+
let ms = tap_script.to_no_checks_ms();
234234
// Creating new contexts is cheap
235235
let secp = bitcoin::secp256k1::Secp256k1::verification_only();
236236
let tap_script = tap_script.encode();
@@ -306,7 +306,7 @@ pub(super) fn from_txdata<'txin>(
306306
// parse wsh with Segwitv0 context
307307
let miniscript = script_from_stackelem::<Segwitv0>(&elem)?;
308308
let script = miniscript.encode();
309-
let miniscript = pre_taproot_to_no_checks(&miniscript);
309+
let miniscript = miniscript.to_no_checks_ms();
310310
let scripthash = sha256::Hash::hash(&script[..]);
311311
if slice
312312
== &bitcoin::Script::new_v0_p2wsh(&scripthash.into())[..]
@@ -328,7 +328,7 @@ pub(super) fn from_txdata<'txin>(
328328
// normal p2sh parsed in Legacy context
329329
let miniscript = script_from_stackelem::<Legacy>(&elem)?;
330330
let script = miniscript.encode();
331-
let miniscript = pre_taproot_to_no_checks(&miniscript);
331+
let miniscript = miniscript.to_no_checks_ms();
332332
if wit_stack.is_empty() {
333333
let scripthash = hash160::Hash::hash(&script[..]);
334334
if *spk == bitcoin::Script::new_p2sh(&scripthash.into()) {
@@ -351,7 +351,7 @@ pub(super) fn from_txdata<'txin>(
351351
if wit_stack.is_empty() {
352352
// Bare script parsed in BareCtx
353353
let miniscript = Miniscript::<bitcoin::PublicKey, BareCtx>::parse_insane(spk)?;
354-
let miniscript = pre_taproot_to_no_checks(&miniscript);
354+
let miniscript = miniscript.to_no_checks_ms();
355355
Ok((
356356
Inner::Script(miniscript, ScriptType::Bare),
357357
ssig_stack,
@@ -363,29 +363,39 @@ pub(super) fn from_txdata<'txin>(
363363
}
364364
}
365365

366-
// Convert a miniscript from a specified context into an insane miniscript
367-
// We need to remember how the hash160 was translated because while doing a checksig
368-
// we need to know whether to parse the public key provided in witness as x-only or full
369-
pub(super) fn pre_taproot_to_no_checks<Ctx: ScriptContext>(
370-
ms: &Miniscript<bitcoin::PublicKey, Ctx>,
371-
) -> Miniscript<BitcoinKey, NoChecks> {
372-
// specify the () error type as this cannot error
373-
ms.real_translate_pk::<_, _, _, (), _>(&mut |pk| Ok(BitcoinKey::Fullkey(*pk)), &mut |pkh| {
374-
Ok(TaggedHash160::FullKey(*pkh))
375-
})
376-
.expect("Translation should succeed")
366+
// Convert a miniscript from a well-defined context to a no checks context.
367+
// We need to parse insane scripts because these scripts are obtained from already
368+
// created transaction possibly already confirmed in a block.
369+
// In order to avoid code duplication for various contexts related interpreter checks,
370+
// we convert all the scripts to from a well-defined context to NoContexts.
371+
//
372+
// While executing Pkh(<hash>) in NoChecks, we need to pop a public key from stack
373+
// However, NoChecks context does not know whether to parse the key as 33 bytes or 32 bytes
374+
// While converting into NoChecks we store explicitly in TypedHash160 enum.
375+
pub(super) trait ToNoChecks {
376+
fn to_no_checks_ms(&self) -> Miniscript<BitcoinKey, NoChecks>;
377377
}
378378

379-
// Convert a miniscript from a specified context into an insane miniscript
380-
pub(super) fn taproot_to_no_checks<Ctx: ScriptContext>(
381-
ms: &Miniscript<bitcoin::XOnlyPublicKey, Ctx>,
382-
) -> Miniscript<BitcoinKey, NoChecks> {
383-
// specify the () error type as this cannot error
384-
ms.real_translate_pk::<_, _, _, (), _>(
385-
&mut |xpk| Ok(BitcoinKey::XOnlyPublicKey(*xpk)),
386-
&mut |pkh| Ok(TaggedHash160::XonlyKey(*pkh)),
387-
)
388-
.expect("Translation should succeed")
379+
impl<Ctx: ScriptContext> ToNoChecks for Miniscript<bitcoin::PublicKey, Ctx> {
380+
fn to_no_checks_ms(&self) -> Miniscript<BitcoinKey, NoChecks> {
381+
// specify the () error type as this cannot error
382+
self.real_translate_pk::<_, _, _, (), _>(
383+
&mut |pk| Ok(BitcoinKey::Fullkey(*pk)),
384+
&mut |pkh| Ok(TypedHash160::FullKey(*pkh)),
385+
)
386+
.expect("Translation should succeed")
387+
}
388+
}
389+
390+
impl<Ctx: ScriptContext> ToNoChecks for Miniscript<bitcoin::XOnlyPublicKey, Ctx> {
391+
fn to_no_checks_ms(&self) -> Miniscript<BitcoinKey, NoChecks> {
392+
// specify the () error type as this cannot error
393+
self.real_translate_pk::<_, _, _, (), _>(
394+
&mut |xpk| Ok(BitcoinKey::XOnlyPublicKey(*xpk)),
395+
&mut |pkh| Ok(TypedHash160::XonlyKey(*pkh)),
396+
)
397+
.expect("Translation should succeed")
398+
}
389399
}
390400

391401
#[cfg(test)]
@@ -737,7 +747,7 @@ mod tests {
737747
fn ms_inner_script(ms: &str) -> (Miniscript<BitcoinKey, NoChecks>, bitcoin::Script) {
738748
let ms = Miniscript::<bitcoin::PublicKey, Segwitv0>::from_str_insane(ms).unwrap();
739749
let spk = ms.encode();
740-
let miniscript = pre_taproot_to_no_checks(&ms);
750+
let miniscript = ms.to_no_checks_ms();
741751
(miniscript, spk)
742752
}
743753
#[test]

src/interpreter/mod.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ impl KeySigPair {
9191
// - This does not implement ToPublicKey to avoid context dependant encoding/decoding of 33/32
9292
// byte keys. This allows us to keep a single NoChecks context instead of a context for
9393
// for NoChecksSchnorr/NoChecksEcdsa.
94-
// Long term TODO: There really should be any need for Miniscript<Pk: MiniscriptKey> struct
94+
// Long term TODO: There really should be not be any need for Miniscript<Pk: MiniscriptKey> struct
9595
// to have the Pk: MiniscriptKey bound. The bound should be on all of it's methods. That would
9696
// require changing Miniscript struct to three generics Miniscript<Pk, Pkh, Ctx> and bound on
9797
// all of the methods of Miniscript to ensure that Pkh = Pk::Hash
@@ -127,34 +127,34 @@ impl From<bitcoin::XOnlyPublicKey> for BitcoinKey {
127127

128128
// While parsing we need to remember how to the hash was parsed so that we can
129129
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
130-
enum TaggedHash160 {
130+
enum TypedHash160 {
131131
XonlyKey(hash160::Hash),
132132
FullKey(hash160::Hash),
133133
}
134134

135-
impl fmt::Display for TaggedHash160 {
135+
impl fmt::Display for TypedHash160 {
136136
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137137
match self {
138-
TaggedHash160::FullKey(pkh) | TaggedHash160::XonlyKey(pkh) => pkh.fmt(f),
138+
TypedHash160::FullKey(pkh) | TypedHash160::XonlyKey(pkh) => pkh.fmt(f),
139139
}
140140
}
141141
}
142142

143-
impl TaggedHash160 {
143+
impl TypedHash160 {
144144
fn hash160(&self) -> hash160::Hash {
145145
match self {
146-
TaggedHash160::XonlyKey(hash) | TaggedHash160::FullKey(hash) => *hash,
146+
TypedHash160::XonlyKey(hash) | TypedHash160::FullKey(hash) => *hash,
147147
}
148148
}
149149
}
150150

151151
impl MiniscriptKey for BitcoinKey {
152-
type Hash = TaggedHash160;
152+
type Hash = TypedHash160;
153153

154154
fn to_pubkeyhash(&self) -> Self::Hash {
155155
match self {
156-
BitcoinKey::Fullkey(pk) => TaggedHash160::FullKey(pk.to_pubkeyhash()),
157-
BitcoinKey::XOnlyPublicKey(pk) => TaggedHash160::XonlyKey(pk.to_pubkeyhash()),
156+
BitcoinKey::Fullkey(pk) => TypedHash160::FullKey(pk.to_pubkeyhash()),
157+
BitcoinKey::XOnlyPublicKey(pk) => TypedHash160::XonlyKey(pk.to_pubkeyhash()),
158158
}
159159
}
160160
}
@@ -988,6 +988,7 @@ fn verify_sersig<'txin>(
988988
#[cfg(test)]
989989
mod tests {
990990

991+
use super::inner::ToNoChecks;
991992
use super::*;
992993
use bitcoin;
993994
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
@@ -1460,6 +1461,6 @@ mod tests {
14601461
fn no_checks_ms(ms: &str) -> Miniscript<BitcoinKey, NoChecks> {
14611462
let elem: Miniscript<bitcoin::PublicKey, NoChecks> =
14621463
Miniscript::from_str_insane(ms).unwrap();
1463-
inner::pre_taproot_to_no_checks(&elem)
1464+
elem.to_no_checks_ms()
14641465
}
14651466
}

src/interpreter/stack.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d, Hash};
2020

2121
use super::error::PkEvalErrInner;
2222
use super::{
23-
verify_sersig, BitcoinKey, Error, HashLockType, KeySigPair, SatisfiedConstraint, TaggedHash160,
23+
verify_sersig, BitcoinKey, Error, HashLockType, KeySigPair, SatisfiedConstraint, TypedHash160,
2424
};
2525

2626
/// Definition of Stack Element of the Stack used for interpretation of Miniscript.
@@ -175,17 +175,17 @@ impl<'txin> Stack<'txin> {
175175
pub(super) fn evaluate_pkh<'intp>(
176176
&mut self,
177177
verify_sig: &mut Box<dyn FnMut(&KeySigPair) -> bool + 'intp>,
178-
pkh: &'intp TaggedHash160,
178+
pkh: &'intp TypedHash160,
179179
) -> Option<Result<SatisfiedConstraint, Error>> {
180180
// Parse a bitcoin key from witness data slice depending on hash context
181181
// when we encounter a pkh(hash)
182182
// Depending on the tag of hash, we parse the as full key or x-only-key
183183
// TODO: All keys parse errors are currently captured in a single BadPubErr
184184
// We don't really store information about which key error.
185-
fn bitcoin_key_from_slice(sl: &[u8], tag: TaggedHash160) -> Option<BitcoinKey> {
185+
fn bitcoin_key_from_slice(sl: &[u8], tag: TypedHash160) -> Option<BitcoinKey> {
186186
let key: BitcoinKey = match tag {
187-
TaggedHash160::XonlyKey(_) => bitcoin::XOnlyPublicKey::from_slice(sl).ok()?.into(),
188-
TaggedHash160::FullKey(_) => bitcoin::PublicKey::from_slice(sl).ok()?.into(),
187+
TypedHash160::XonlyKey(_) => bitcoin::XOnlyPublicKey::from_slice(sl).ok()?.into(),
188+
TypedHash160::FullKey(_) => bitcoin::PublicKey::from_slice(sl).ok()?.into(),
189189
};
190190
Some(key)
191191
}

0 commit comments

Comments
 (0)