Skip to content

Commit 51263b4

Browse files
committed
Add support for KeyExpr in taproot compiler
1 parent 0b4d3b3 commit 51263b4

File tree

3 files changed

+361
-44
lines changed

3 files changed

+361
-44
lines changed

src/policy/compiler.rs

+154-12
Original file line numberDiff line numberDiff line change
@@ -868,10 +868,26 @@ where
868868
let mut q_zero_right = best_compilations(policy_cache, &subs[1], sat_prob, None)?;
869869
let mut q_zero_left = best_compilations(policy_cache, &subs[0], sat_prob, None)?;
870870

871-
compile_binary!(&mut left, &mut right, [1.0, 1.0], Terminal::AndB);
872-
compile_binary!(&mut right, &mut left, [1.0, 1.0], Terminal::AndB);
873-
compile_binary!(&mut left, &mut right, [1.0, 1.0], Terminal::AndV);
874-
compile_binary!(&mut right, &mut left, [1.0, 1.0], Terminal::AndV);
871+
let key_vec: Vec<Pk> = subs
872+
.iter()
873+
.filter_map(|pol|
874+
if let Concrete::Key(ref pk) = *pol {
875+
Some(pk.clone())
876+
} else {
877+
None
878+
}
879+
)
880+
.collect();
881+
if key_vec.len() == 2 {
882+
let musig_vec = key_vec.into_iter().map(|pk| KeyExpr::SingleKey(pk)).collect();
883+
insert_wrap!(AstElemExt::terminal(Terminal::PkK(KeyExpr::MuSig(musig_vec))));
884+
} else {
885+
compile_binary!(&mut left, &mut right, [1.0, 1.0], Terminal::AndB);
886+
compile_binary!(&mut right, &mut left, [1.0, 1.0], Terminal::AndB);
887+
compile_binary!(&mut left, &mut right, [1.0, 1.0], Terminal::AndV);
888+
compile_binary!(&mut right, &mut left, [1.0, 1.0], Terminal::AndV);
889+
}
890+
875891
let mut zero_comp = BTreeMap::new();
876892
zero_comp.insert(
877893
CompilationKey::from_type(
@@ -885,6 +901,7 @@ where
885901
compile_tern!(&mut right, &mut q_zero_left, &mut zero_comp, [1.0, 0.0]);
886902
}
887903
Concrete::Or(ref subs) => {
904+
assert_eq!(subs.len(), 2, "or takes 2 args");
888905
let total = (subs[0].0 + subs[1].0) as f64;
889906
let lw = subs[0].0 as f64 / total;
890907
let rw = subs[1].0 as f64 / total;
@@ -1039,7 +1056,11 @@ where
10391056
for key in key_vec {
10401057
k_vec.push(KeyExpr::SingleKey(key))
10411058
}
1042-
insert_wrap!(AstElemExt::terminal(Terminal::MultiA(k, k_vec)))
1059+
if k == k_vec.len() {
1060+
insert_wrap!(AstElemExt::terminal(Terminal::PkK(KeyExpr::MuSig(k_vec))))
1061+
} else {
1062+
insert_wrap!(AstElemExt::terminal(Terminal::MultiA(k, k_vec)))
1063+
}
10431064
}
10441065
SigType::Ecdsa
10451066
if key_vec.len() == subs.len() && subs.len() <= MAX_PUBKEYS_PER_MULTISIG =>
@@ -1216,7 +1237,7 @@ mod tests {
12161237
use crate::policy::Liftable;
12171238
use crate::script_num_size;
12181239

1219-
type SPolicy = Concrete<String>;
1240+
type StringPolicy = Concrete<String>;
12201241
type BPolicy = Concrete<bitcoin::PublicKey>;
12211242
type DummyTapAstElemExt = policy::compiler::AstElemExt<String, Tap>;
12221243
type SegwitMiniScript = Miniscript<bitcoin::PublicKey, Segwitv0>;
@@ -1247,7 +1268,7 @@ mod tests {
12471268
}
12481269

12491270
fn policy_compile_lift_check(s: &str) -> Result<(), CompilerError> {
1250-
let policy = SPolicy::from_str(s).expect("parse");
1271+
let policy = StringPolicy::from_str(s).expect("parse");
12511272
let miniscript: Miniscript<String, Segwitv0> = policy.compile()?;
12521273

12531274
assert_eq!(
@@ -1257,18 +1278,139 @@ mod tests {
12571278
Ok(())
12581279
}
12591280

1281+
#[test]
1282+
fn compile_to_musig() {
1283+
let pol: StringPolicy =
1284+
StringPolicy::from_str("thresh(3,pk(A),pk(B),pk(C))").unwrap();
1285+
let output = pol.compile::<Tap>();
1286+
println!("The miniscript is {}", output.unwrap());
1287+
1288+
let pol: StringPolicy =
1289+
StringPolicy::from_str("and(pk(A),pk(B))").unwrap();
1290+
let output = pol.compile::<Tap>();
1291+
println!("The miniscript is {}", output.unwrap());
1292+
1293+
let pol: StringPolicy =
1294+
StringPolicy::from_str("thresh(2,thresh(2,pk(A),pk(B)),pk(C),pk(D))").unwrap();
1295+
let output = pol.compile::<Tap>();
1296+
println!("The miniscript is {}", output.unwrap());
1297+
1298+
let pol: StringPolicy =
1299+
StringPolicy::from_str("thresh(2,pk(A),pk(B),pk(C))").unwrap();
1300+
let output = pol.compile::<Segwitv0>();
1301+
println!("The miniscript is {}", output.unwrap());
1302+
1303+
let pol: StringPolicy =
1304+
StringPolicy::from_str("thresh(2,pk(A),pk(B),pk(C))").unwrap();
1305+
let output = pol.compile::<Tap>();
1306+
println!("The miniscript is {}", output.unwrap());
1307+
}
1308+
1309+
#[test]
1310+
fn test_internal_key_extraction() {
1311+
let pol: StringPolicy =
1312+
StringPolicy::from_str("thresh(1,and(pk(A1),pk(A2)),thresh(3,pk(A6),pk(A3),pk(A4)),pk(A5))").unwrap();
1313+
let output = pol.compile::<Tap>().unwrap();
1314+
println!("The miniscript is {}", output);
1315+
let taproot = pol.compile_tr(Some("UNSPENDABLE_KEY".to_string())).unwrap();
1316+
// Internal key => pk(A5)
1317+
println!("The taproot descriptor is {}", taproot);
1318+
1319+
let pol: StringPolicy =
1320+
StringPolicy::from_str("thresh(1,and(pk(A1),pk(A2)),thresh(3,pk(A6),pk(A3),pk(A4)),and(pk(A5),sha256(H)))").unwrap();
1321+
let output = pol.compile::<Tap>().unwrap();
1322+
println!("The miniscript is {}", output);
1323+
let taproot = pol.compile_tr(Some("UNSPENDABLE_KEY".to_string())).unwrap();
1324+
// Internal key should be => musig(A1,A2)
1325+
println!("The taproot descriptor is {}", taproot);
1326+
1327+
let pol: StringPolicy =
1328+
StringPolicy::from_str("thresh(1,and(pk(A1),older(9)),and(pk(A2),sha256(H)))").unwrap();
1329+
let output = pol.compile::<Tap>().unwrap();
1330+
println!("The miniscript is {}", output);
1331+
let taproot = pol.compile_tr(Some("UNSPENDABLE_KEY".to_string())).unwrap();
1332+
// Internal key should be => musig(A1,A2)
1333+
println!("The taproot descriptor is {}", taproot);
1334+
1335+
let pol_str = "or(
1336+
3@or(
1337+
4@thresh(1, pk(A1), and(pk(A2), pk(A3))),
1338+
5@thresh(1, or(pk(A4), pk(A5)), and(pk(A6), pk(A7)))
1339+
),
1340+
2@or(
1341+
2@thresh(3, and(pk(A8), pk(A9)), or(pk(A10), pk(A11)), and(pk(A12), sha256(H1))),
1342+
1@or(
1343+
4@and(pk(A13), sha256(H2)),
1344+
1@thresh(1, or(pk(A14), pk(A15)), and(pk(A16), pk(A17)))
1345+
)
1346+
)
1347+
)";
1348+
let pol_str = pol_str.replace(&[' ', '\n'][..], "");
1349+
let pol: StringPolicy =
1350+
StringPolicy::from_str(pol_str.as_str()).unwrap();
1351+
let output = pol.compile::<Tap>().unwrap();
1352+
println!("The miniscript is {}", output);
1353+
let taproot = pol.compile_tr(Some("UNSPENDABLE_KEY".to_string())).unwrap();
1354+
// Internal key should be => musig(A6,A7)
1355+
println!("The taproot descriptor is {}", taproot);
1356+
1357+
let pol_str = "or(
1358+
3@or(
1359+
4@thresh(1, pk(A1), and(pk(A2), pk(A3))),
1360+
5@thresh(1, or(pk(A4), pk(A5)), and(pk(A6), pk(A7)))
1361+
),
1362+
4@or(
1363+
2@thresh(3, pk(A8), pk(A9), pk(A10)),
1364+
1@or(
1365+
4@and(pk(A13), sha256(H2)),
1366+
1@thresh(1, or(pk(A14), pk(A15)), and(pk(A16), pk(A17)))
1367+
)
1368+
)
1369+
)";
1370+
let pol_str = pol_str.replace(&[' ', '\n'][..], "");
1371+
let pol: StringPolicy =
1372+
StringPolicy::from_str(pol_str.as_str()).unwrap();
1373+
let output = pol.compile::<Tap>().unwrap();
1374+
println!("The miniscript is {}", output);
1375+
let taproot = pol.compile_tr(Some("UNSPENDABLE_KEY".to_string())).unwrap();
1376+
// Internal key should be => musig(A8,A9,A10)
1377+
println!("The taproot descriptor is {}", taproot);
1378+
1379+
let pol_str = "or(
1380+
3@or(
1381+
4@thresh(2, pk(A1), pk(A2), pk(A3)),
1382+
5@thresh(2, or(pk(A4), pk(A5)), and(pk(A6), pk(A7)), pk(A18))
1383+
),
1384+
4@or(
1385+
2@thresh(2, pk(A8), pk(A9), pk(A10)),
1386+
1@or(
1387+
4@and(pk(A13), sha256(H2)),
1388+
1@thresh(1, or(pk(A14), pk(A15)), and(pk(A16), pk(A17)))
1389+
)
1390+
)
1391+
)";
1392+
let pol_str = pol_str.replace(&[' ', '\n'][..], "");
1393+
let pol: StringPolicy =
1394+
StringPolicy::from_str(pol_str.as_str()).unwrap();
1395+
let output = pol.compile::<Tap>().unwrap();
1396+
println!("The miniscript is {}", output);
1397+
let taproot = pol.compile_tr(Some("UNSPENDABLE_KEY".to_string())).unwrap();
1398+
// Internal key should be => musig(A8,A9)
1399+
println!("The taproot descriptor is {}", taproot);
1400+
}
1401+
12601402
#[test]
12611403
fn compile_timelocks() {
12621404
// artificially create a policy that is problematic and try to compile
1263-
let pol: SPolicy = Concrete::And(vec![
1405+
let pol: StringPolicy = Concrete::And(vec![
12641406
Concrete::Key("A".to_string()),
12651407
Concrete::And(vec![Concrete::After(9), Concrete::After(1000_000_000)]),
12661408
]);
12671409
assert!(pol.compile::<Segwitv0>().is_err());
12681410

12691411
// This should compile
1270-
let pol: SPolicy =
1271-
SPolicy::from_str("and(pk(A),or(and(after(9),pk(B)),and(after(1000000000),pk(C))))")
1412+
let pol: StringPolicy =
1413+
StringPolicy::from_str("and(pk(A),or(and(after(9),pk(B)),and(after(1000000000),pk(C))))")
12721414
.unwrap();
12731415
assert!(pol.compile::<Segwitv0>().is_ok());
12741416
}
@@ -1307,7 +1449,7 @@ mod tests {
13071449

13081450
#[test]
13091451
fn compile_q() {
1310-
let policy = SPolicy::from_str("or(1@and(pk(A),pk(B)),127@pk(C))").expect("parsing");
1452+
let policy = StringPolicy::from_str("or(1@and(pk(A),pk(B)),127@pk(C))").expect("parsing");
13111453
let compilation: DummyTapAstElemExt =
13121454
best_t(&mut BTreeMap::new(), &policy, 1.0, None).unwrap();
13131455

@@ -1318,7 +1460,7 @@ mod tests {
13181460
);
13191461

13201462
// compile into taproot context to avoid limit errors
1321-
let policy = SPolicy::from_str(
1463+
let policy = StringPolicy::from_str(
13221464
"and(and(and(or(127@thresh(2,pk(A),pk(B),thresh(2,or(127@pk(A),1@pk(B)),after(100),or(and(pk(C),after(200)),and(pk(D),sha256(66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925))),pk(E))),1@pk(F)),sha256(66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925)),or(127@pk(G),1@after(300))),or(127@after(400),pk(H)))"
13231465
).expect("parsing");
13241466
let compilation: DummyTapAstElemExt =

0 commit comments

Comments
 (0)