Skip to content

Commit c42b1cb

Browse files
committed
Merge #594: Implement TreeLike for policy::Concrete
1efc7a3 Refactor check_timelocks helper (Tobin C. Harding) 4749931 Use TreeLike to implement check_timelocks_helper (Tobin C. Harding) 1cbc018 Add unit test for check_timelocks API (Tobin C. Harding) 64488d6 Use TreeLike to implement translate_pk (Tobin C. Harding) 3282193 Add basic unit test for translate_pk (Tobin C. Harding) c81389f Use TreeLike to implement for_each_key (Tobin C. Harding) b3c0955 Add unit test coverage for_each_key (Tobin C. Harding) 9280db2 Use TreeLike to implement num_tap_leaves (Tobin C. Harding) 3fc604a Add basic unit test for num_tap_leaves (Tobin C. Harding) b7f8939 Use TreeLike to implement keys (Tobin C. Harding) 984576c Add basic unit test for keys (Tobin C. Harding) 2b11f64 Use TreeLike to implement translate_unsatisfiable_pk (Tobin C. Harding) 4d22908 Add basic unit test for translate_unsatisfiable_pk (Tobin C. Harding) 578dc69 Remove code comment from soon-to-be duplicated pattern (Tobin C. Harding) 277b587 Implement TreeLike for policy::Concrete (Tobin C. Harding) 82c0eee Remove PolicyArc (Tobin C. Harding) Pull request description: Make a start on removing recursion in the `policy::concrete` module. - Patch 1: Remove`PolicyArc` - Patch2: Implement `TreeLike` for `concrete::Policy` (does not use it) - Patch 3: Trivial code comment deletion - Subsequent patches are pairs - Add a basic unit test for the function we are about to patch - Remove recursion from a function (the one we just tested) - Final patch is a refactoring There is still some to go but this is already a pretty heavy review burden. ACKs for top commit: apoelstra: ACK 1efc7a3 sanket1729: ACK 1efc7a3 Tree-SHA512: 529fcf60adda1ff43a95ee896dccac746870b527fec5a638e2377206220aa87369132d037a44a048ca25fc67df1b6309d53d8fdfb7134299421396538ec043d4
2 parents 3c6ae26 + 1efc7a3 commit c42b1cb

File tree

5 files changed

+360
-337
lines changed

5 files changed

+360
-337
lines changed

Diff for: src/iter/mod.rs

+27-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub use tree::{
1515
};
1616

1717
use crate::sync::Arc;
18-
use crate::{Miniscript, MiniscriptKey, ScriptContext, Terminal};
18+
use crate::{policy, Miniscript, MiniscriptKey, ScriptContext, Terminal};
1919

2020
impl<'a, Pk: MiniscriptKey, Ctx: ScriptContext> TreeLike for &'a Miniscript<Pk, Ctx> {
2121
fn as_node(&self) -> Tree<Self> {
@@ -68,3 +68,29 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> TreeLike for Arc<Miniscript<Pk, Ctx>
6868
}
6969
}
7070
}
71+
72+
impl<'a, Pk: MiniscriptKey> TreeLike for &'a policy::Concrete<Pk> {
73+
fn as_node(&self) -> Tree<Self> {
74+
use policy::Concrete::*;
75+
match *self {
76+
Unsatisfiable | Trivial | Key(_) | After(_) | Older(_) | Sha256(_) | Hash256(_)
77+
| Ripemd160(_) | Hash160(_) => Tree::Nullary,
78+
And(ref subs) => Tree::Nary(subs.iter().map(Arc::as_ref).collect()),
79+
Or(ref v) => Tree::Nary(v.iter().map(|(_, p)| p.as_ref()).collect()),
80+
Threshold(_, ref subs) => Tree::Nary(subs.iter().map(Arc::as_ref).collect()),
81+
}
82+
}
83+
}
84+
85+
impl<'a, Pk: MiniscriptKey> TreeLike for Arc<policy::Concrete<Pk>> {
86+
fn as_node(&self) -> Tree<Self> {
87+
use policy::Concrete::*;
88+
match self.as_ref() {
89+
Unsatisfiable | Trivial | Key(_) | After(_) | Older(_) | Sha256(_) | Hash256(_)
90+
| Ripemd160(_) | Hash160(_) => Tree::Nullary,
91+
And(ref subs) => Tree::Nary(subs.iter().map(Arc::clone).collect()),
92+
Or(ref v) => Tree::Nary(v.iter().map(|(_, p)| Arc::clone(p)).collect()),
93+
Threshold(_, ref subs) => Tree::Nary(subs.iter().map(Arc::clone).collect()),
94+
}
95+
}
96+
}

Diff for: src/miniscript/mod.rs

-1
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,6 @@ impl<Pk: MiniscriptKey, Ctx: ScriptContext> Miniscript<Pk, Ctx> {
441441
{
442442
let mut translated = vec![];
443443
for data in Arc::new(self.clone()).post_order_iter() {
444-
// convenience method to reduce typing
445444
let child_n = |n| Arc::clone(&translated[data.child_indices[n]]);
446445

447446
let new_term = match data.node.node {

Diff for: src/policy/compiler.rs

+92-48
Original file line numberDiff line numberDiff line change
@@ -786,10 +786,14 @@ where
786786
}
787787
Concrete::And(ref subs) => {
788788
assert_eq!(subs.len(), 2, "and takes 2 args");
789-
let mut left = best_compilations(policy_cache, &subs[0], sat_prob, dissat_prob)?;
790-
let mut right = best_compilations(policy_cache, &subs[1], sat_prob, dissat_prob)?;
791-
let mut q_zero_right = best_compilations(policy_cache, &subs[1], sat_prob, None)?;
792-
let mut q_zero_left = best_compilations(policy_cache, &subs[0], sat_prob, None)?;
789+
let mut left =
790+
best_compilations(policy_cache, subs[0].as_ref(), sat_prob, dissat_prob)?;
791+
let mut right =
792+
best_compilations(policy_cache, subs[1].as_ref(), sat_prob, dissat_prob)?;
793+
let mut q_zero_right =
794+
best_compilations(policy_cache, subs[1].as_ref(), sat_prob, None)?;
795+
let mut q_zero_left =
796+
best_compilations(policy_cache, subs[0].as_ref(), sat_prob, None)?;
793797

794798
compile_binary!(&mut left, &mut right, [1.0, 1.0], Terminal::AndB);
795799
compile_binary!(&mut right, &mut left, [1.0, 1.0], Terminal::AndB);
@@ -813,48 +817,56 @@ where
813817
let rw = subs[1].0 as f64 / total;
814818

815819
//and-or
816-
if let (Concrete::And(x), _) = (&subs[0].1, &subs[1].1) {
820+
if let (Concrete::And(x), _) = (subs[0].1.as_ref(), subs[1].1.as_ref()) {
817821
let mut a1 = best_compilations(
818822
policy_cache,
819-
&x[0],
823+
x[0].as_ref(),
820824
lw * sat_prob,
821825
Some(dissat_prob.unwrap_or(0 as f64) + rw * sat_prob),
822826
)?;
823-
let mut a2 = best_compilations(policy_cache, &x[0], lw * sat_prob, None)?;
827+
let mut a2 = best_compilations(policy_cache, x[0].as_ref(), lw * sat_prob, None)?;
824828

825829
let mut b1 = best_compilations(
826830
policy_cache,
827-
&x[1],
831+
x[1].as_ref(),
828832
lw * sat_prob,
829833
Some(dissat_prob.unwrap_or(0 as f64) + rw * sat_prob),
830834
)?;
831-
let mut b2 = best_compilations(policy_cache, &x[1], lw * sat_prob, None)?;
835+
let mut b2 = best_compilations(policy_cache, x[1].as_ref(), lw * sat_prob, None)?;
832836

833-
let mut c =
834-
best_compilations(policy_cache, &subs[1].1, rw * sat_prob, dissat_prob)?;
837+
let mut c = best_compilations(
838+
policy_cache,
839+
subs[1].1.as_ref(),
840+
rw * sat_prob,
841+
dissat_prob,
842+
)?;
835843

836844
compile_tern!(&mut a1, &mut b2, &mut c, [lw, rw]);
837845
compile_tern!(&mut b1, &mut a2, &mut c, [lw, rw]);
838846
};
839-
if let (_, Concrete::And(x)) = (&subs[0].1, &subs[1].1) {
847+
if let (_, Concrete::And(x)) = (&subs[0].1.as_ref(), subs[1].1.as_ref()) {
840848
let mut a1 = best_compilations(
841849
policy_cache,
842-
&x[0],
850+
x[0].as_ref(),
843851
rw * sat_prob,
844852
Some(dissat_prob.unwrap_or(0 as f64) + lw * sat_prob),
845853
)?;
846-
let mut a2 = best_compilations(policy_cache, &x[0], rw * sat_prob, None)?;
854+
let mut a2 = best_compilations(policy_cache, x[0].as_ref(), rw * sat_prob, None)?;
847855

848856
let mut b1 = best_compilations(
849857
policy_cache,
850-
&x[1],
858+
x[1].as_ref(),
851859
rw * sat_prob,
852860
Some(dissat_prob.unwrap_or(0 as f64) + lw * sat_prob),
853861
)?;
854-
let mut b2 = best_compilations(policy_cache, &x[1], rw * sat_prob, None)?;
862+
let mut b2 = best_compilations(policy_cache, x[1].as_ref(), rw * sat_prob, None)?;
855863

856-
let mut c =
857-
best_compilations(policy_cache, &subs[0].1, lw * sat_prob, dissat_prob)?;
864+
let mut c = best_compilations(
865+
policy_cache,
866+
subs[0].1.as_ref(),
867+
lw * sat_prob,
868+
dissat_prob,
869+
)?;
858870

859871
compile_tern!(&mut a1, &mut b2, &mut c, [rw, lw]);
860872
compile_tern!(&mut b1, &mut a2, &mut c, [rw, lw]);
@@ -873,12 +885,22 @@ where
873885
let mut r_comp = vec![];
874886

875887
for dissat_prob in dissat_probs(rw).iter() {
876-
let l = best_compilations(policy_cache, &subs[0].1, lw * sat_prob, *dissat_prob)?;
888+
let l = best_compilations(
889+
policy_cache,
890+
subs[0].1.as_ref(),
891+
lw * sat_prob,
892+
*dissat_prob,
893+
)?;
877894
l_comp.push(l);
878895
}
879896

880897
for dissat_prob in dissat_probs(lw).iter() {
881-
let r = best_compilations(policy_cache, &subs[1].1, rw * sat_prob, *dissat_prob)?;
898+
let r = best_compilations(
899+
policy_cache,
900+
subs[1].1.as_ref(),
901+
rw * sat_prob,
902+
*dissat_prob,
903+
)?;
882904
r_comp.push(r);
883905
}
884906

@@ -913,8 +935,8 @@ where
913935
let sp = sat_prob * k_over_n;
914936
//Expressions must be dissatisfiable
915937
let dp = Some(dissat_prob.unwrap_or(0 as f64) + (1.0 - k_over_n) * sat_prob);
916-
let be = best(types::Base::B, policy_cache, ast, sp, dp)?;
917-
let bw = best(types::Base::W, policy_cache, ast, sp, dp)?;
938+
let be = best(types::Base::B, policy_cache, ast.as_ref(), sp, dp)?;
939+
let bw = best(types::Base::W, policy_cache, ast.as_ref(), sp, dp)?;
918940

919941
let diff = be.cost_1d(sp, dp) - bw.cost_1d(sp, dp);
920942
best_es.push((be.comp_ext_data, be));
@@ -947,7 +969,7 @@ where
947969
let key_vec: Vec<Pk> = subs
948970
.iter()
949971
.filter_map(|s| {
950-
if let Concrete::Key(ref pk) = *s {
972+
if let Concrete::Key(ref pk) = s.as_ref() {
951973
Some(pk.clone())
952974
} else {
953975
None
@@ -967,9 +989,10 @@ where
967989
_ if k == subs.len() => {
968990
let mut it = subs.iter();
969991
let mut policy = it.next().expect("No sub policy in thresh() ?").clone();
970-
policy = it.fold(policy, |acc, pol| Concrete::And(vec![acc, pol.clone()]));
992+
policy =
993+
it.fold(policy, |acc, pol| Concrete::And(vec![acc, pol.clone()]).into());
971994

972-
ret = best_compilations(policy_cache, &policy, sat_prob, dissat_prob)?;
995+
ret = best_compilations(policy_cache, policy.as_ref(), sat_prob, dissat_prob)?;
973996
}
974997
_ => {}
975998
}
@@ -1178,8 +1201,11 @@ mod tests {
11781201
fn compile_timelocks() {
11791202
// artificially create a policy that is problematic and try to compile
11801203
let pol: SPolicy = Concrete::And(vec![
1181-
Concrete::Key("A".to_string()),
1182-
Concrete::And(vec![Concrete::after(9), Concrete::after(1000_000_000)]),
1204+
Arc::new(Concrete::Key("A".to_string())),
1205+
Arc::new(Concrete::And(vec![
1206+
Arc::new(Concrete::after(9)),
1207+
Arc::new(Concrete::after(1000_000_000)),
1208+
])),
11831209
]);
11841210
assert!(pol.compile::<Segwitv0>().is_err());
11851211

@@ -1273,13 +1299,22 @@ mod tests {
12731299

12741300
// Liquid policy
12751301
let policy: BPolicy = Concrete::Or(vec![
1276-
(127, Concrete::Threshold(3, key_pol[0..5].to_owned())),
1302+
(
1303+
127,
1304+
Arc::new(Concrete::Threshold(
1305+
3,
1306+
key_pol[0..5].iter().map(|p| (p.clone()).into()).collect(),
1307+
)),
1308+
),
12771309
(
12781310
1,
1279-
Concrete::And(vec![
1280-
Concrete::Older(Sequence::from_height(10000)),
1281-
Concrete::Threshold(2, key_pol[5..8].to_owned()),
1282-
]),
1311+
Arc::new(Concrete::And(vec![
1312+
Arc::new(Concrete::Older(Sequence::from_height(10000))),
1313+
Arc::new(Concrete::Threshold(
1314+
2,
1315+
key_pol[5..8].iter().map(|p| (p.clone()).into()).collect(),
1316+
)),
1317+
])),
12831318
),
12841319
]);
12851320

@@ -1391,8 +1426,10 @@ mod tests {
13911426
// and to a ms thresh otherwise.
13921427
// k = 1 (or 2) does not compile, see https://github.com/rust-bitcoin/rust-miniscript/issues/114
13931428
for k in &[10, 15, 21] {
1394-
let pubkeys: Vec<Concrete<bitcoin::PublicKey>> =
1395-
keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect();
1429+
let pubkeys: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys
1430+
.iter()
1431+
.map(|pubkey| Arc::new(Concrete::Key(*pubkey)))
1432+
.collect();
13961433
let big_thresh = Concrete::Threshold(*k, pubkeys);
13971434
let big_thresh_ms: SegwitMiniScript = big_thresh.compile().unwrap();
13981435
if *k == 21 {
@@ -1419,18 +1456,18 @@ mod tests {
14191456
// or(thresh(52, [pubkey; 52]), thresh(52, [pubkey; 52])) results in a 3642-bytes long
14201457
// witness script with only 54 stack elements
14211458
let (keys, _) = pubkeys_and_a_sig(104);
1422-
let keys_a: Vec<Concrete<bitcoin::PublicKey>> = keys[..keys.len() / 2]
1459+
let keys_a: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys[..keys.len() / 2]
14231460
.iter()
1424-
.map(|pubkey| Concrete::Key(*pubkey))
1461+
.map(|pubkey| Arc::new(Concrete::Key(*pubkey)))
14251462
.collect();
1426-
let keys_b: Vec<Concrete<bitcoin::PublicKey>> = keys[keys.len() / 2..]
1463+
let keys_b: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys[keys.len() / 2..]
14271464
.iter()
1428-
.map(|pubkey| Concrete::Key(*pubkey))
1465+
.map(|pubkey| Arc::new(Concrete::Key(*pubkey)))
14291466
.collect();
14301467

14311468
let thresh_res: Result<SegwitMiniScript, _> = Concrete::Or(vec![
1432-
(1, Concrete::Threshold(keys_a.len(), keys_a)),
1433-
(1, Concrete::Threshold(keys_b.len(), keys_b)),
1469+
(1, Arc::new(Concrete::Threshold(keys_a.len(), keys_a))),
1470+
(1, Arc::new(Concrete::Threshold(keys_b.len(), keys_b))),
14341471
])
14351472
.compile();
14361473
let script_size = thresh_res.clone().and_then(|m| Ok(m.script_size()));
@@ -1443,8 +1480,10 @@ mod tests {
14431480

14441481
// Hit the maximum witness stack elements limit
14451482
let (keys, _) = pubkeys_and_a_sig(100);
1446-
let keys: Vec<Concrete<bitcoin::PublicKey>> =
1447-
keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect();
1483+
let keys: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys
1484+
.iter()
1485+
.map(|pubkey| Arc::new(Concrete::Key(*pubkey)))
1486+
.collect();
14481487
let thresh_res: Result<SegwitMiniScript, _> =
14491488
Concrete::Threshold(keys.len(), keys).compile();
14501489
let n_elements = thresh_res
@@ -1462,8 +1501,10 @@ mod tests {
14621501
fn shared_limits() {
14631502
// Test the maximum number of OPs with a 67-of-68 multisig
14641503
let (keys, _) = pubkeys_and_a_sig(68);
1465-
let keys: Vec<Concrete<bitcoin::PublicKey>> =
1466-
keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect();
1504+
let keys: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys
1505+
.iter()
1506+
.map(|pubkey| Arc::new(Concrete::Key(*pubkey)))
1507+
.collect();
14671508
let thresh_res: Result<SegwitMiniScript, _> =
14681509
Concrete::Threshold(keys.len() - 1, keys).compile();
14691510
let ops_count = thresh_res.clone().and_then(|m| Ok(m.ext.ops.op_count()));
@@ -1475,8 +1516,10 @@ mod tests {
14751516
);
14761517
// For legacy too..
14771518
let (keys, _) = pubkeys_and_a_sig(68);
1478-
let keys: Vec<Concrete<bitcoin::PublicKey>> =
1479-
keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect();
1519+
let keys: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys
1520+
.iter()
1521+
.map(|pubkey| Arc::new(Concrete::Key(*pubkey)))
1522+
.collect();
14801523
let thresh_res = Concrete::Threshold(keys.len() - 1, keys).compile::<Legacy>();
14811524
let ops_count = thresh_res.clone().and_then(|m| Ok(m.ext.ops.op_count()));
14821525
assert_eq!(
@@ -1488,8 +1531,9 @@ mod tests {
14881531

14891532
// Test that we refuse to compile policies with duplicated keys
14901533
let (keys, _) = pubkeys_and_a_sig(1);
1491-
let key = Concrete::Key(keys[0]);
1492-
let res = Concrete::Or(vec![(1, key.clone()), (1, key.clone())]).compile::<Segwitv0>();
1534+
let key = Arc::new(Concrete::Key(keys[0]));
1535+
let res =
1536+
Concrete::Or(vec![(1, Arc::clone(&key)), (1, Arc::clone(&key))]).compile::<Segwitv0>();
14931537
assert_eq!(
14941538
res,
14951539
Err(CompilerError::PolicyError(policy::concrete::PolicyError::DuplicatePubKeys))

0 commit comments

Comments
 (0)