Skip to content

Commit 4c4597e

Browse files
committedMar 4, 2024··
Merge #649: Refactor out type_check
5f41cb6 change FnMut -> Fn (Riccardo Casatta) cdcd53e Refactor out type_check (Riccardo Casatta) Pull request description: This apply the same logic of ElementsProject/elements-miniscript#74 removing a couple of unreachable, simplify things and very likely reduce binary bloat. ~~However, in doing so, I don't think this MR is equivalent to master for the logic used for `get_child`.~~ ~~I suspect current master is broken and this fix it (or viceversa?), but I have to suspend this work for now.~~ ACKs for top commit: apoelstra: ACK 5f41cb6 this is great, thanks! Tree-SHA512: 4b904fde55ba75895d377babde5e7a3a215cdf3b0ccff0c90572bb286de9d8a80234fc6a4921ad83bcb8c947affa1e3c23b2135534cb4363f38f1bab89368ba4
2 parents 551932e + 5f41cb6 commit 4c4597e

File tree

6 files changed

+183
-200
lines changed

6 files changed

+183
-200
lines changed
 

‎src/miniscript/astelem.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ use bitcoin::{absolute, opcodes, script, Sequence};
1515
use sync::Arc;
1616

1717
use crate::miniscript::context::SigType;
18-
use crate::miniscript::types::{self, Property};
19-
use crate::miniscript::ScriptContext;
18+
use crate::miniscript::{types, ScriptContext};
2019
use crate::prelude::*;
2120
use crate::util::MsKeyBuilder;
2221
use crate::{

‎src/miniscript/decode.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use sync::Arc;
1717
use crate::miniscript::lex::{Token as Tk, TokenIter};
1818
use crate::miniscript::limits::MAX_PUBKEYS_PER_MULTISIG;
1919
use crate::miniscript::types::extra_props::ExtData;
20-
use crate::miniscript::types::{Property, Type};
20+
use crate::miniscript::types::Type;
2121
use crate::miniscript::ScriptContext;
2222
use crate::prelude::*;
2323
#[cfg(doc)]

‎src/miniscript/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ use core::cmp;
4040
use sync::Arc;
4141

4242
use self::lex::{lex, TokenIter};
43-
use self::types::Property;
4443
pub use crate::miniscript::context::ScriptContext;
4544
use crate::miniscript::decode::Terminal;
4645
use crate::miniscript::types::extra_props::ExtData;

‎src/miniscript/types/extra_props.rs

+3-13
Original file line numberDiff line numberDiff line change
@@ -856,22 +856,12 @@ impl Property for ExtData {
856856
exec_stack_elem_count_dissat,
857857
})
858858
}
859+
}
859860

860-
fn type_check_with_child<Pk, Ctx, C>(
861-
_fragment: &Terminal<Pk, Ctx>,
862-
mut _child: C,
863-
) -> Result<Self, Error>
864-
where
865-
C: FnMut(usize) -> Self,
866-
Pk: MiniscriptKey,
867-
Ctx: ScriptContext,
868-
{
869-
unreachable!()
870-
}
871-
861+
impl ExtData {
872862
/// Compute the type of a fragment assuming all the children of
873863
/// Miniscript have been computed already.
874-
fn type_check<Pk, Ctx>(fragment: &Terminal<Pk, Ctx>) -> Result<Self, Error>
864+
pub fn type_check<Pk, Ctx>(fragment: &Terminal<Pk, Ctx>) -> Result<Self, Error>
875865
where
876866
Ctx: ScriptContext,
877867
Pk: MiniscriptKey,

‎src/miniscript/types/mod.rs

+3-182
Original file line numberDiff line numberDiff line change
@@ -346,175 +346,6 @@ pub trait Property: Sized {
346346
fn threshold<S>(k: usize, n: usize, sub_ck: S) -> Result<Self, ErrorKind>
347347
where
348348
S: FnMut(usize) -> Result<Self, ErrorKind>;
349-
350-
/// Compute the type of a fragment, given a function to look up
351-
/// the types of its children, if available and relevant for the
352-
/// given fragment
353-
fn type_check_common<'a, Pk, Ctx, C>(
354-
fragment: &'a Terminal<Pk, Ctx>,
355-
mut get_child: C,
356-
) -> Result<Self, Error>
357-
where
358-
C: FnMut(&'a Terminal<Pk, Ctx>, usize) -> Result<Self, Error>,
359-
Pk: MiniscriptKey,
360-
Ctx: ScriptContext,
361-
{
362-
let wrap_err = |result: Result<Self, ErrorKind>| {
363-
result.map_err(|kind| Error { fragment_string: fragment.to_string(), error: kind })
364-
};
365-
366-
let ret = match *fragment {
367-
Terminal::True => Ok(Self::from_true()),
368-
Terminal::False => Ok(Self::from_false()),
369-
Terminal::PkK(..) => Ok(Self::from_pk_k::<Ctx>()),
370-
Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::<Ctx>()),
371-
Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => {
372-
if k == 0 {
373-
return Err(Error {
374-
fragment_string: fragment.to_string(),
375-
error: ErrorKind::ZeroThreshold,
376-
});
377-
}
378-
if k > pks.len() {
379-
return Err(Error {
380-
fragment_string: fragment.to_string(),
381-
error: ErrorKind::OverThreshold(k, pks.len()),
382-
});
383-
}
384-
match *fragment {
385-
Terminal::Multi(..) => Ok(Self::from_multi(k, pks.len())),
386-
Terminal::MultiA(..) => Ok(Self::from_multi_a(k, pks.len())),
387-
_ => unreachable!(),
388-
}
389-
}
390-
Terminal::After(t) => {
391-
// Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The
392-
// number on the stack would be a 5 bytes signed integer but Miniscript's B type
393-
// only consumes 4 bytes from the stack.
394-
if t == absolute::LockTime::ZERO.into() {
395-
return Err(Error {
396-
fragment_string: fragment.to_string(),
397-
error: ErrorKind::InvalidTime,
398-
});
399-
}
400-
Ok(Self::from_after(t.into()))
401-
}
402-
Terminal::Older(t) => {
403-
if t == Sequence::ZERO || !t.is_relative_lock_time() {
404-
return Err(Error {
405-
fragment_string: fragment.to_string(),
406-
error: ErrorKind::InvalidTime,
407-
});
408-
}
409-
Ok(Self::from_older(t))
410-
}
411-
Terminal::Sha256(..) => Ok(Self::from_sha256()),
412-
Terminal::Hash256(..) => Ok(Self::from_hash256()),
413-
Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()),
414-
Terminal::Hash160(..) => Ok(Self::from_hash160()),
415-
Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(get_child(&sub.node, 0)?)),
416-
Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(get_child(&sub.node, 0)?)),
417-
Terminal::Check(ref sub) => wrap_err(Self::cast_check(get_child(&sub.node, 0)?)),
418-
Terminal::DupIf(ref sub) => wrap_err(Self::cast_dupif(get_child(&sub.node, 0)?)),
419-
Terminal::Verify(ref sub) => wrap_err(Self::cast_verify(get_child(&sub.node, 0)?)),
420-
Terminal::NonZero(ref sub) => wrap_err(Self::cast_nonzero(get_child(&sub.node, 0)?)),
421-
Terminal::ZeroNotEqual(ref sub) => {
422-
wrap_err(Self::cast_zeronotequal(get_child(&sub.node, 0)?))
423-
}
424-
Terminal::AndB(ref l, ref r) => {
425-
let ltype = get_child(&l.node, 0)?;
426-
let rtype = get_child(&r.node, 1)?;
427-
wrap_err(Self::and_b(ltype, rtype))
428-
}
429-
Terminal::AndV(ref l, ref r) => {
430-
let ltype = get_child(&l.node, 0)?;
431-
let rtype = get_child(&r.node, 1)?;
432-
wrap_err(Self::and_v(ltype, rtype))
433-
}
434-
Terminal::OrB(ref l, ref r) => {
435-
let ltype = get_child(&l.node, 0)?;
436-
let rtype = get_child(&r.node, 1)?;
437-
wrap_err(Self::or_b(ltype, rtype))
438-
}
439-
Terminal::OrD(ref l, ref r) => {
440-
let ltype = get_child(&l.node, 0)?;
441-
let rtype = get_child(&r.node, 1)?;
442-
wrap_err(Self::or_d(ltype, rtype))
443-
}
444-
Terminal::OrC(ref l, ref r) => {
445-
let ltype = get_child(&l.node, 0)?;
446-
let rtype = get_child(&r.node, 1)?;
447-
wrap_err(Self::or_c(ltype, rtype))
448-
}
449-
Terminal::OrI(ref l, ref r) => {
450-
let ltype = get_child(&l.node, 0)?;
451-
let rtype = get_child(&r.node, 1)?;
452-
wrap_err(Self::or_i(ltype, rtype))
453-
}
454-
Terminal::AndOr(ref a, ref b, ref c) => {
455-
let atype = get_child(&a.node, 0)?;
456-
let btype = get_child(&b.node, 1)?;
457-
let ctype = get_child(&c.node, 2)?;
458-
wrap_err(Self::and_or(atype, btype, ctype))
459-
}
460-
Terminal::Thresh(k, ref subs) => {
461-
if k == 0 {
462-
return Err(Error {
463-
fragment_string: fragment.to_string(),
464-
error: ErrorKind::ZeroThreshold,
465-
});
466-
}
467-
if k > subs.len() {
468-
return Err(Error {
469-
fragment_string: fragment.to_string(),
470-
error: ErrorKind::OverThreshold(k, subs.len()),
471-
});
472-
}
473-
474-
let mut last_err_frag = None;
475-
let res = Self::threshold(k, subs.len(), |n| match get_child(&subs[n].node, n) {
476-
Ok(x) => Ok(x),
477-
Err(e) => {
478-
last_err_frag = Some(e.fragment_string);
479-
Err(e.error)
480-
}
481-
});
482-
483-
res.map_err(|kind| Error {
484-
fragment_string: last_err_frag.unwrap_or_else(|| fragment.to_string()),
485-
error: kind,
486-
})
487-
}
488-
};
489-
if let Ok(ref ret) = ret {
490-
ret.sanity_checks()
491-
}
492-
ret
493-
}
494-
495-
/// Compute the type of a fragment, given a function to look up
496-
/// the types of its children.
497-
fn type_check_with_child<Pk, Ctx, C>(
498-
fragment: &Terminal<Pk, Ctx>,
499-
mut child: C,
500-
) -> Result<Self, Error>
501-
where
502-
C: FnMut(usize) -> Self,
503-
Pk: MiniscriptKey,
504-
Ctx: ScriptContext,
505-
{
506-
let get_child = |_sub, n| Ok(child(n));
507-
Self::type_check_common(fragment, get_child)
508-
}
509-
510-
/// Compute the type of a fragment.
511-
fn type_check<Pk, Ctx>(fragment: &Terminal<Pk, Ctx>) -> Result<Self, Error>
512-
where
513-
Pk: MiniscriptKey,
514-
Ctx: ScriptContext,
515-
{
516-
Self::type_check_common(fragment, |sub, _n| Self::type_check(sub))
517-
}
518349
}
519350

520351
impl Property for Type {
@@ -695,22 +526,12 @@ impl Property for Type {
695526
mall: Property::threshold(k, n, |n| Ok(sub_ck(n)?.mall))?,
696527
})
697528
}
529+
}
698530

699-
fn type_check_with_child<Pk, Ctx, C>(
700-
_fragment: &Terminal<Pk, Ctx>,
701-
mut _child: C,
702-
) -> Result<Self, Error>
703-
where
704-
C: FnMut(usize) -> Self,
705-
Pk: MiniscriptKey,
706-
Ctx: ScriptContext,
707-
{
708-
unreachable!()
709-
}
710-
531+
impl Type {
711532
/// Compute the type of a fragment assuming all the children of
712533
/// Miniscript have been computed already.
713-
fn type_check<Pk, Ctx>(fragment: &Terminal<Pk, Ctx>) -> Result<Self, Error>
534+
pub fn type_check<Pk, Ctx>(fragment: &Terminal<Pk, Ctx>) -> Result<Self, Error>
714535
where
715536
Pk: MiniscriptKey,
716537
Ctx: ScriptContext,

‎src/policy/compiler.rs

+175-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use core::{cmp, f64, fmt, hash, mem};
99
#[cfg(feature = "std")]
1010
use std::error;
1111

12+
use bitcoin::{absolute, Sequence};
1213
use sync::Arc;
1314

1415
use crate::miniscript::context::SigType;
@@ -413,6 +414,179 @@ impl Property for CompilerExtData {
413414
}
414415
}
415416

417+
impl CompilerExtData {
418+
/// Compute the type of a fragment, given a function to look up
419+
/// the types of its children.
420+
fn type_check_with_child<Pk, Ctx, C>(
421+
fragment: &Terminal<Pk, Ctx>,
422+
child: C,
423+
) -> Result<Self, types::Error>
424+
where
425+
C: Fn(usize) -> Self,
426+
Pk: MiniscriptKey,
427+
Ctx: ScriptContext,
428+
{
429+
let get_child = |_sub, n| Ok(child(n));
430+
Self::type_check_common(fragment, get_child)
431+
}
432+
433+
/// Compute the type of a fragment.
434+
fn type_check<Pk, Ctx>(fragment: &Terminal<Pk, Ctx>) -> Result<Self, types::Error>
435+
where
436+
Pk: MiniscriptKey,
437+
Ctx: ScriptContext,
438+
{
439+
let check_child = |sub, _n| Self::type_check(sub);
440+
Self::type_check_common(fragment, check_child)
441+
}
442+
443+
/// Compute the type of a fragment, given a function to look up
444+
/// the types of its children, if available and relevant for the
445+
/// given fragment
446+
fn type_check_common<'a, Pk, Ctx, C>(
447+
fragment: &'a Terminal<Pk, Ctx>,
448+
get_child: C,
449+
) -> Result<Self, types::Error>
450+
where
451+
C: Fn(&'a Terminal<Pk, Ctx>, usize) -> Result<Self, types::Error>,
452+
Pk: MiniscriptKey,
453+
Ctx: ScriptContext,
454+
{
455+
let wrap_err = |result: Result<Self, ErrorKind>| {
456+
result
457+
.map_err(|kind| types::Error { fragment_string: fragment.to_string(), error: kind })
458+
};
459+
460+
let ret = match *fragment {
461+
Terminal::True => Ok(Self::from_true()),
462+
Terminal::False => Ok(Self::from_false()),
463+
Terminal::PkK(..) => Ok(Self::from_pk_k::<Ctx>()),
464+
Terminal::PkH(..) | Terminal::RawPkH(..) => Ok(Self::from_pk_h::<Ctx>()),
465+
Terminal::Multi(k, ref pks) | Terminal::MultiA(k, ref pks) => {
466+
if k == 0 {
467+
return Err(types::Error {
468+
fragment_string: fragment.to_string(),
469+
error: types::ErrorKind::ZeroThreshold,
470+
});
471+
}
472+
if k > pks.len() {
473+
return Err(types::Error {
474+
fragment_string: fragment.to_string(),
475+
error: types::ErrorKind::OverThreshold(k, pks.len()),
476+
});
477+
}
478+
match *fragment {
479+
Terminal::Multi(..) => Ok(Self::from_multi(k, pks.len())),
480+
Terminal::MultiA(..) => Ok(Self::from_multi_a(k, pks.len())),
481+
_ => unreachable!(),
482+
}
483+
}
484+
Terminal::After(t) => {
485+
// Note that for CLTV this is a limitation not of Bitcoin but Miniscript. The
486+
// number on the stack would be a 5 bytes signed integer but Miniscript's B type
487+
// only consumes 4 bytes from the stack.
488+
if t == absolute::LockTime::ZERO.into() {
489+
return Err(types::Error {
490+
fragment_string: fragment.to_string(),
491+
error: types::ErrorKind::InvalidTime,
492+
});
493+
}
494+
Ok(Self::from_after(t.into()))
495+
}
496+
Terminal::Older(t) => {
497+
if t == Sequence::ZERO || !t.is_relative_lock_time() {
498+
return Err(types::Error {
499+
fragment_string: fragment.to_string(),
500+
error: types::ErrorKind::InvalidTime,
501+
});
502+
}
503+
Ok(Self::from_older(t))
504+
}
505+
Terminal::Sha256(..) => Ok(Self::from_sha256()),
506+
Terminal::Hash256(..) => Ok(Self::from_hash256()),
507+
Terminal::Ripemd160(..) => Ok(Self::from_ripemd160()),
508+
Terminal::Hash160(..) => Ok(Self::from_hash160()),
509+
Terminal::Alt(ref sub) => wrap_err(Self::cast_alt(get_child(&sub.node, 0)?)),
510+
Terminal::Swap(ref sub) => wrap_err(Self::cast_swap(get_child(&sub.node, 0)?)),
511+
Terminal::Check(ref sub) => wrap_err(Self::cast_check(get_child(&sub.node, 0)?)),
512+
Terminal::DupIf(ref sub) => wrap_err(Self::cast_dupif(get_child(&sub.node, 0)?)),
513+
Terminal::Verify(ref sub) => wrap_err(Self::cast_verify(get_child(&sub.node, 0)?)),
514+
Terminal::NonZero(ref sub) => wrap_err(Self::cast_nonzero(get_child(&sub.node, 0)?)),
515+
Terminal::ZeroNotEqual(ref sub) => {
516+
wrap_err(Self::cast_zeronotequal(get_child(&sub.node, 0)?))
517+
}
518+
Terminal::AndB(ref l, ref r) => {
519+
let ltype = get_child(&l.node, 0)?;
520+
let rtype = get_child(&r.node, 1)?;
521+
wrap_err(Self::and_b(ltype, rtype))
522+
}
523+
Terminal::AndV(ref l, ref r) => {
524+
let ltype = get_child(&l.node, 0)?;
525+
let rtype = get_child(&r.node, 1)?;
526+
wrap_err(Self::and_v(ltype, rtype))
527+
}
528+
Terminal::OrB(ref l, ref r) => {
529+
let ltype = get_child(&l.node, 0)?;
530+
let rtype = get_child(&r.node, 1)?;
531+
wrap_err(Self::or_b(ltype, rtype))
532+
}
533+
Terminal::OrD(ref l, ref r) => {
534+
let ltype = get_child(&l.node, 0)?;
535+
let rtype = get_child(&r.node, 1)?;
536+
wrap_err(Self::or_d(ltype, rtype))
537+
}
538+
Terminal::OrC(ref l, ref r) => {
539+
let ltype = get_child(&l.node, 0)?;
540+
let rtype = get_child(&r.node, 1)?;
541+
wrap_err(Self::or_c(ltype, rtype))
542+
}
543+
Terminal::OrI(ref l, ref r) => {
544+
let ltype = get_child(&l.node, 0)?;
545+
let rtype = get_child(&r.node, 1)?;
546+
wrap_err(Self::or_i(ltype, rtype))
547+
}
548+
Terminal::AndOr(ref a, ref b, ref c) => {
549+
let atype = get_child(&a.node, 0)?;
550+
let btype = get_child(&b.node, 1)?;
551+
let ctype = get_child(&c.node, 2)?;
552+
wrap_err(Self::and_or(atype, btype, ctype))
553+
}
554+
Terminal::Thresh(k, ref subs) => {
555+
if k == 0 {
556+
return Err(types::Error {
557+
fragment_string: fragment.to_string(),
558+
error: types::ErrorKind::ZeroThreshold,
559+
});
560+
}
561+
if k > subs.len() {
562+
return Err(types::Error {
563+
fragment_string: fragment.to_string(),
564+
error: types::ErrorKind::OverThreshold(k, subs.len()),
565+
});
566+
}
567+
568+
let mut last_err_frag = None;
569+
let res = Self::threshold(k, subs.len(), |n| match get_child(&subs[n].node, n) {
570+
Ok(x) => Ok(x),
571+
Err(e) => {
572+
last_err_frag = Some(e.fragment_string);
573+
Err(e.error)
574+
}
575+
});
576+
577+
res.map_err(|kind| types::Error {
578+
fragment_string: last_err_frag.unwrap_or_else(|| fragment.to_string()),
579+
error: kind,
580+
})
581+
}
582+
};
583+
if let Ok(ref ret) = ret {
584+
ret.sanity_checks()
585+
}
586+
ret
587+
}
588+
}
589+
416590
/// Miniscript AST fragment with additional data needed by the compiler
417591
#[derive(Clone, Debug)]
418592
struct AstElemExt<Pk: MiniscriptKey, Ctx: ScriptContext> {
@@ -1151,7 +1325,7 @@ mod tests {
11511325
use core::str::FromStr;
11521326

11531327
use bitcoin::blockdata::{opcodes, script};
1154-
use bitcoin::{hashes, Sequence};
1328+
use bitcoin::hashes;
11551329

11561330
use super::*;
11571331
use crate::miniscript::{Legacy, Segwitv0, Tap};

0 commit comments

Comments
 (0)
Please sign in to comment.