Skip to content

Commit 7475b3d

Browse files
committed
compiler: replace conjuctions in AstElemExt::type_check with explicit functions
Conjunctions don't need to set 'branch_prob', but we do set them, leading to confusing logic. By getting rid of these, we can make the conjunction logic simpler and avoid setting branch_prob to Some(0.0).
1 parent 393bbd1 commit 7475b3d

1 file changed

Lines changed: 137 additions & 22 deletions

File tree

src/policy/compiler.rs

Lines changed: 137 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use sync::Arc;
1313

1414
use crate::miniscript::context::SigType;
1515
use crate::miniscript::limits::{MAX_PUBKEYS_IN_CHECKSIGADD, MAX_PUBKEYS_PER_MULTISIG};
16-
use crate::miniscript::types::{self, ErrorKind, ExtData, Type};
16+
use crate::miniscript::types::{self, ErrorKind, Type};
1717
use crate::miniscript::ScriptContext;
1818
use crate::policy::Concrete;
1919
use crate::prelude::*;
@@ -353,7 +353,7 @@ impl CompilerExtData {
353353
fn and_or(a: Self, b: Self, c: Self) -> Self {
354354
let aprob = a.branch_prob.expect("andor, a prob must be set");
355355
let bprob = b.branch_prob.expect("andor, b prob must be set");
356-
let cprob = c.branch_prob.expect("andor, c prob must be set");
356+
let cprob = c.branch_prob.unwrap_or(0.0);
357357

358358
let adis = a
359359
.dissat_cost
@@ -562,6 +562,55 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> AstElemExt<Pk, Ctx> {
562562
}
563563
}
564564

565+
/// Helper functions to compose two Miniscript fragments, where we assume
566+
/// by construction that all validation parameters are upheld.
567+
fn compose_typeck_only(
568+
term: Terminal<Pk, Ctx>,
569+
) -> Result<Arc<Miniscript<Pk, Ctx>>, types::Error> {
570+
let ty = types::Type::type_check(&term)?;
571+
let ext = types::ExtData::type_check(&term);
572+
Ok(Arc::new(Miniscript::from_components_unchecked(term, ty, ext)))
573+
}
574+
575+
fn and_b(left: &Self, right: &Self) -> Result<Self, types::Error> {
576+
Ok(Self {
577+
ms: Self::compose_typeck_only(Terminal::AndB(
578+
Arc::clone(&left.ms),
579+
Arc::clone(&right.ms),
580+
))?,
581+
comp_ext_data: CompilerExtData::and_b(left.comp_ext_data, right.comp_ext_data),
582+
})
583+
}
584+
585+
fn and_v(left: &Self, right: &Self) -> Result<Self, types::Error> {
586+
Ok(Self {
587+
ms: Self::compose_typeck_only(Terminal::AndV(
588+
Arc::clone(&left.ms),
589+
Arc::clone(&right.ms),
590+
))?,
591+
comp_ext_data: CompilerExtData::and_v(left.comp_ext_data, right.comp_ext_data),
592+
})
593+
}
594+
595+
/// andor(a,b,0) is a conjunction of a and b
596+
fn and_or_0(left: &Self, right: &Self) -> Result<Self, types::Error> {
597+
let left_ext_data = CompilerExtData { branch_prob: Some(1.0), ..left.comp_ext_data };
598+
let right_ext_data = CompilerExtData { branch_prob: Some(1.0), ..right.comp_ext_data };
599+
600+
Ok(Self {
601+
ms: Self::compose_typeck_only(Terminal::AndOr(
602+
Arc::clone(&left.ms),
603+
Arc::clone(&right.ms),
604+
Arc::new(Miniscript::FALSE),
605+
))?,
606+
comp_ext_data: CompilerExtData::and_or(
607+
left_ext_data,
608+
right_ext_data,
609+
CompilerExtData::FALSE,
610+
),
611+
})
612+
}
613+
565614
fn binary(ast: Terminal<Pk, Ctx>, l: &Self, r: &Self) -> Result<Self, types::Error> {
566615
let lookup_ext = |n| match n {
567616
0 => l.comp_ext_data,
@@ -861,26 +910,92 @@ where
861910
Concrete::Hash160(ref hash) => insert_wrap!(AstElemExt::hash160(hash.clone())),
862911
Concrete::And(ref subs) => {
863912
assert_eq!(subs.len(), 2, "and takes 2 args");
864-
let mut left =
865-
best_compilations(policy_cache, subs[0].as_ref(), sat_prob, dissat_prob)?;
866-
let mut right =
867-
best_compilations(policy_cache, subs[1].as_ref(), sat_prob, dissat_prob)?;
868-
let mut q_zero_right =
869-
best_compilations(policy_cache, subs[1].as_ref(), sat_prob, None)?;
870-
let mut q_zero_left =
871-
best_compilations(policy_cache, subs[0].as_ref(), sat_prob, None)?;
872-
873-
compile_binary!(&mut left, &mut right, [1.0, 1.0], Terminal::AndB);
874-
compile_binary!(&mut right, &mut left, [1.0, 1.0], Terminal::AndB);
875-
compile_binary!(&mut left, &mut right, [1.0, 1.0], Terminal::AndV);
876-
compile_binary!(&mut right, &mut left, [1.0, 1.0], Terminal::AndV);
877-
let mut zero_comp = BTreeMap::new();
878-
zero_comp.insert(
879-
CompilationKey::from_type(Type::FALSE, ExtData::FALSE.has_free_verify, dissat_prob),
880-
AstElemExt::unsatisfiable(),
881-
);
882-
compile_tern!(&mut left, &mut q_zero_right, &mut zero_comp, [1.0, 0.0]);
883-
compile_tern!(&mut right, &mut q_zero_left, &mut zero_comp, [1.0, 0.0]);
913+
let left = best_compilations(policy_cache, subs[0].as_ref(), sat_prob, dissat_prob)?;
914+
let right = best_compilations(policy_cache, subs[1].as_ref(), sat_prob, dissat_prob)?;
915+
let q_zero_right = best_compilations(policy_cache, subs[1].as_ref(), sat_prob, None)?;
916+
let q_zero_left = best_compilations(policy_cache, subs[0].as_ref(), sat_prob, None)?;
917+
918+
for l in left.values() {
919+
for r in right.values() {
920+
if let Ok(new_ext) = AstElemExt::and_b(l, r) {
921+
insert_best_wrapped(
922+
policy_cache,
923+
policy,
924+
&mut ret,
925+
new_ext,
926+
sat_prob,
927+
dissat_prob,
928+
)?;
929+
}
930+
if let Ok(new_ext) = AstElemExt::and_v(l, r) {
931+
insert_best_wrapped(
932+
policy_cache,
933+
policy,
934+
&mut ret,
935+
new_ext,
936+
sat_prob,
937+
dissat_prob,
938+
)?;
939+
}
940+
}
941+
}
942+
// Do a separate loop with 'l' and 'r' swapped; we could combine the loops,
943+
// but this would sometimes result in compiling e.g. and(pk(A),pk(B)) into
944+
// an and with A and B swapped, which is surprising to the user since the
945+
// cost is the same with or without the swap.
946+
for l in left.values() {
947+
for r in right.values() {
948+
if let Ok(new_ext) = AstElemExt::and_b(r, l) {
949+
insert_best_wrapped(
950+
policy_cache,
951+
policy,
952+
&mut ret,
953+
new_ext,
954+
sat_prob,
955+
dissat_prob,
956+
)?;
957+
}
958+
if let Ok(new_ext) = AstElemExt::and_v(r, l) {
959+
insert_best_wrapped(
960+
policy_cache,
961+
policy,
962+
&mut ret,
963+
new_ext,
964+
sat_prob,
965+
dissat_prob,
966+
)?;
967+
}
968+
}
969+
}
970+
971+
for l in left.values() {
972+
for r in q_zero_right.values() {
973+
if let Ok(new_ext) = AstElemExt::and_or_0(l, r) {
974+
insert_best_wrapped(
975+
policy_cache,
976+
policy,
977+
&mut ret,
978+
new_ext,
979+
sat_prob,
980+
dissat_prob,
981+
)?;
982+
}
983+
}
984+
}
985+
for l in right.values() {
986+
for r in q_zero_left.values() {
987+
if let Ok(new_ext) = AstElemExt::and_or_0(l, r) {
988+
insert_best_wrapped(
989+
policy_cache,
990+
policy,
991+
&mut ret,
992+
new_ext,
993+
sat_prob,
994+
dissat_prob,
995+
)?;
996+
}
997+
}
998+
}
884999
}
8851000
Concrete::Or(ref subs) => {
8861001
let total = u32::from(subs[0].0) as f64 + u32::from(subs[1].0) as f64;

0 commit comments

Comments
 (0)