Skip to content

Commit 74cafab

Browse files
committed
Remove Concrete translation recursion
Remove recursion in the two translation functions for `policy::Concrete`. Requires implementing `TreeLike` for `policy::Concrete` as we do for `Miniscript`. And then remove the now duplicate `PolicyArc`. Note this does not remove _all_ recursion in the `concrete` module (see for example `num_tap_leaves`).
1 parent df9fffb commit 74cafab

File tree

4 files changed

+170
-251
lines changed

4 files changed

+170
-251
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)| Arc::as_ref(p)).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/policy/compiler.rs

+44-28
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ where
871871
let rw = subs[1].0 as f64 / total;
872872

873873
//and-or
874-
if let (Concrete::And(x), _) = (&subs[0].1, &subs[1].1) {
874+
if let (Concrete::And(x), _) = (&subs[0].1.as_ref(), &subs[1].1) {
875875
let mut a1 = best_compilations(
876876
policy_cache,
877877
&x[0],
@@ -894,7 +894,7 @@ where
894894
compile_tern!(&mut a1, &mut b2, &mut c, [lw, rw]);
895895
compile_tern!(&mut b1, &mut a2, &mut c, [lw, rw]);
896896
};
897-
if let (_, Concrete::And(x)) = (&subs[0].1, &subs[1].1) {
897+
if let (_, Concrete::And(x)) = (&subs[0].1, &subs[1].1.as_ref()) {
898898
let mut a1 = best_compilations(
899899
policy_cache,
900900
&x[0],
@@ -1005,7 +1005,7 @@ where
10051005
let key_vec: Vec<Pk> = subs
10061006
.iter()
10071007
.filter_map(|s| {
1008-
if let Concrete::Key(ref pk) = *s {
1008+
if let Concrete::Key(ref pk) = s.as_ref() {
10091009
Some(pk.clone())
10101010
} else {
10111011
None
@@ -1025,7 +1025,9 @@ where
10251025
_ if k == subs.len() => {
10261026
let mut it = subs.iter();
10271027
let mut policy = it.next().expect("No sub policy in thresh() ?").clone();
1028-
policy = it.fold(policy, |acc, pol| Concrete::And(vec![acc, pol.clone()]));
1028+
policy = it.fold(policy, |acc, pol| {
1029+
Arc::new(Concrete::And(vec![acc, pol.clone()]))
1030+
});
10291031

10301032
ret = best_compilations(policy_cache, &policy, sat_prob, dissat_prob)?;
10311033
}
@@ -1239,8 +1241,11 @@ mod tests {
12391241
fn compile_timelocks() {
12401242
// artificially create a policy that is problematic and try to compile
12411243
let pol: SPolicy = Concrete::And(vec![
1242-
Concrete::Key("A".to_string()),
1243-
Concrete::And(vec![Concrete::after(9), Concrete::after(1000_000_000)]),
1244+
Arc::new(Concrete::Key("A".to_string())),
1245+
Arc::new(Concrete::And(vec![
1246+
Arc::new(Concrete::after(9)),
1247+
Arc::new(Concrete::after(1000_000_000)),
1248+
])),
12441249
]);
12451250
assert!(pol.compile::<Segwitv0>().is_err());
12461251

@@ -1310,7 +1315,7 @@ mod tests {
13101315
#[test]
13111316
fn compile_misc() {
13121317
let (keys, sig) = pubkeys_and_a_sig(10);
1313-
let key_pol: Vec<BPolicy> = keys.iter().map(|k| Concrete::Key(*k)).collect();
1318+
let key_pol: Vec<Arc<BPolicy>> = keys.iter().map(|k| Arc::new(Concrete::Key(*k))).collect();
13141319

13151320
let policy: BPolicy = Concrete::Key(keys[0].clone());
13161321
let ms: SegwitMiniScript = policy.compile().unwrap();
@@ -1346,13 +1351,16 @@ mod tests {
13461351

13471352
// Liquid policy
13481353
let policy: BPolicy = Concrete::Or(vec![
1349-
(127, Concrete::Threshold(3, key_pol[0..5].to_owned())),
1354+
(
1355+
127,
1356+
Arc::new(Concrete::Threshold(3, key_pol[0..5].to_owned())),
1357+
),
13501358
(
13511359
1,
1352-
Concrete::And(vec![
1353-
Concrete::Older(Sequence::from_height(10000)),
1354-
Concrete::Threshold(2, key_pol[5..8].to_owned()),
1355-
]),
1360+
Arc::new(Concrete::And(vec![
1361+
Arc::new(Concrete::Older(Sequence::from_height(10000))),
1362+
Concrete::Threshold(2, key_pol[5..8].to_owned()).into(),
1363+
])),
13561364
),
13571365
]);
13581366

@@ -1471,8 +1479,10 @@ mod tests {
14711479
// and to a ms thresh otherwise.
14721480
// k = 1 (or 2) does not compile, see https://github.com/rust-bitcoin/rust-miniscript/issues/114
14731481
for k in &[10, 15, 21] {
1474-
let pubkeys: Vec<Concrete<bitcoin::PublicKey>> =
1475-
keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect();
1482+
let pubkeys: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys
1483+
.iter()
1484+
.map(|pubkey| Concrete::Key(*pubkey).into())
1485+
.collect();
14761486
let big_thresh = Concrete::Threshold(*k, pubkeys);
14771487
let big_thresh_ms: SegwitMiniScript = big_thresh.compile().unwrap();
14781488
if *k == 21 {
@@ -1499,18 +1509,18 @@ mod tests {
14991509
// or(thresh(52, [pubkey; 52]), thresh(52, [pubkey; 52])) results in a 3642-bytes long
15001510
// witness script with only 54 stack elements
15011511
let (keys, _) = pubkeys_and_a_sig(104);
1502-
let keys_a: Vec<Concrete<bitcoin::PublicKey>> = keys[..keys.len() / 2]
1512+
let keys_a: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys[..keys.len() / 2]
15031513
.iter()
1504-
.map(|pubkey| Concrete::Key(*pubkey))
1514+
.map(|pubkey| Concrete::Key(*pubkey).into())
15051515
.collect();
1506-
let keys_b: Vec<Concrete<bitcoin::PublicKey>> = keys[keys.len() / 2..]
1516+
let keys_b: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys[keys.len() / 2..]
15071517
.iter()
1508-
.map(|pubkey| Concrete::Key(*pubkey))
1518+
.map(|pubkey| Concrete::Key(*pubkey).into())
15091519
.collect();
15101520

15111521
let thresh_res: Result<SegwitMiniScript, _> = Concrete::Or(vec![
1512-
(1, Concrete::Threshold(keys_a.len(), keys_a)),
1513-
(1, Concrete::Threshold(keys_b.len(), keys_b)),
1522+
(1, Concrete::Threshold(keys_a.len(), keys_a.into()).into()),
1523+
(1, Concrete::Threshold(keys_b.len(), keys_b.into()).into()),
15141524
])
15151525
.compile();
15161526
let script_size = thresh_res.clone().and_then(|m| Ok(m.script_size()));
@@ -1523,8 +1533,10 @@ mod tests {
15231533

15241534
// Hit the maximum witness stack elements limit
15251535
let (keys, _) = pubkeys_and_a_sig(100);
1526-
let keys: Vec<Concrete<bitcoin::PublicKey>> =
1527-
keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect();
1536+
let keys: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys
1537+
.iter()
1538+
.map(|pubkey| Concrete::Key(*pubkey).into())
1539+
.collect();
15281540
let thresh_res: Result<SegwitMiniScript, _> =
15291541
Concrete::Threshold(keys.len(), keys).compile();
15301542
let n_elements = thresh_res
@@ -1542,8 +1554,10 @@ mod tests {
15421554
fn shared_limits() {
15431555
// Test the maximum number of OPs with a 67-of-68 multisig
15441556
let (keys, _) = pubkeys_and_a_sig(68);
1545-
let keys: Vec<Concrete<bitcoin::PublicKey>> =
1546-
keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect();
1557+
let keys: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys
1558+
.iter()
1559+
.map(|pubkey| Concrete::Key(*pubkey).into())
1560+
.collect();
15471561
let thresh_res: Result<SegwitMiniScript, _> =
15481562
Concrete::Threshold(keys.len() - 1, keys).compile();
15491563
let ops_count = thresh_res.clone().and_then(|m| Ok(m.ext.ops.op_count()));
@@ -1555,8 +1569,10 @@ mod tests {
15551569
);
15561570
// For legacy too..
15571571
let (keys, _) = pubkeys_and_a_sig(68);
1558-
let keys: Vec<Concrete<bitcoin::PublicKey>> =
1559-
keys.iter().map(|pubkey| Concrete::Key(*pubkey)).collect();
1572+
let keys: Vec<Arc<Concrete<bitcoin::PublicKey>>> = keys
1573+
.iter()
1574+
.map(|pubkey| Concrete::Key(*pubkey).into())
1575+
.collect();
15601576
let thresh_res = Concrete::Threshold(keys.len() - 1, keys).compile::<Legacy>();
15611577
let ops_count = thresh_res.clone().and_then(|m| Ok(m.ext.ops.op_count()));
15621578
assert_eq!(
@@ -1568,7 +1584,7 @@ mod tests {
15681584

15691585
// Test that we refuse to compile policies with duplicated keys
15701586
let (keys, _) = pubkeys_and_a_sig(1);
1571-
let key = Concrete::Key(keys[0]);
1587+
let key = Arc::new(Concrete::Key(keys[0]));
15721588
let res = Concrete::Or(vec![(1, key.clone()), (1, key.clone())]).compile::<Segwitv0>();
15731589
assert_eq!(
15741590
res,
@@ -1577,7 +1593,7 @@ mod tests {
15771593
))
15781594
);
15791595
// Same for legacy
1580-
let res = Concrete::Or(vec![(1, key.clone()), (1, key)]).compile::<Legacy>();
1596+
let res = Concrete::Or(vec![(1, key.clone()), (1, key.clone())]).compile::<Legacy>();
15811597
assert_eq!(
15821598
res,
15831599
Err(CompilerError::PolicyError(

0 commit comments

Comments
 (0)