Skip to content

Commit bdeb626

Browse files
committed
Only compile polcies containing non repeated keys
Some of the compiler guanrantees are lost if we allow repeated keys. The compiler should error on these keys
1 parent 7301b7b commit bdeb626

File tree

2 files changed

+66
-27
lines changed

2 files changed

+66
-27
lines changed

src/policy/compiler.rs

Lines changed: 24 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,10 +1177,8 @@ mod tests {
11771177
use policy::Liftable;
11781178
use script_num_size;
11791179
use BitcoinSig;
1180-
use DummyKey;
11811180

11821181
type SPolicy = Concrete<String>;
1183-
type DummyPolicy = Concrete<DummyKey>;
11841182
type BPolicy = Concrete<bitcoin::PublicKey>;
11851183
type DummySegwitAstElemExt = policy::compiler::AstElemExt<String, Segwitv0>;
11861184
type SegwitMiniScript = Miniscript<bitcoin::PublicKey, Segwitv0>;
@@ -1211,8 +1209,8 @@ mod tests {
12111209
}
12121210

12131211
fn policy_compile_lift_check(s: &str) -> Result<(), CompilerError> {
1214-
let policy = DummyPolicy::from_str(s).expect("parse");
1215-
let miniscript: Miniscript<DummyKey, Segwitv0> = policy.compile()?;
1212+
let policy = SPolicy::from_str(s).expect("parse");
1213+
let miniscript: Miniscript<String, Segwitv0> = policy.compile()?;
12161214

12171215
assert_eq!(
12181216
policy.lift().unwrap().sorted(),
@@ -1224,21 +1222,21 @@ mod tests {
12241222
#[test]
12251223
fn compile_timelocks() {
12261224
// artificially create a policy that is problematic and try to compile
1227-
let pol: DummyPolicy = Concrete::And(vec![
1228-
Concrete::Key(DummyKey),
1225+
let pol: SPolicy = Concrete::And(vec![
1226+
Concrete::Key("A".to_string()),
12291227
Concrete::And(vec![Concrete::After(9), Concrete::After(1000_000_000)]),
12301228
]);
12311229
assert!(pol.compile::<Segwitv0>().is_err());
12321230

12331231
// This should compile
1234-
let pol: DummyPolicy =
1235-
DummyPolicy::from_str("and(pk(),or(and(after(9),pk()),and(after(1000000000),pk())))")
1232+
let pol: SPolicy =
1233+
SPolicy::from_str("and(pk(A),or(and(after(9),pk(B)),and(after(1000000000),pk(C))))")
12361234
.unwrap();
12371235
assert!(pol.compile::<Segwitv0>().is_ok());
12381236
}
12391237
#[test]
12401238
fn compile_basic() {
1241-
assert!(policy_compile_lift_check("pk()").is_ok());
1239+
assert!(policy_compile_lift_check("pk(A)").is_ok());
12421240
assert_eq!(
12431241
policy_compile_lift_check("after(9)"),
12441242
Err(CompilerError::TopLevelNonSafe)
@@ -1253,24 +1251,24 @@ mod tests {
12531251
),
12541252
Err(CompilerError::TopLevelNonSafe)
12551253
);
1256-
assert!(policy_compile_lift_check("and(pk(),pk())").is_ok());
1257-
assert!(policy_compile_lift_check("or(pk(),pk())").is_ok());
1258-
assert!(policy_compile_lift_check("thresh(2,pk(),pk(),pk())").is_ok());
1254+
assert!(policy_compile_lift_check("and(pk(A),pk(B))").is_ok());
1255+
assert!(policy_compile_lift_check("or(pk(A),pk(B))").is_ok());
1256+
assert!(policy_compile_lift_check("thresh(2,pk(A),pk(B),pk(C))").is_ok());
12591257

12601258
assert_eq!(
1261-
policy_compile_lift_check("thresh(2,after(9),after(9),pk())"),
1259+
policy_compile_lift_check("thresh(2,after(9),after(9),pk(A))"),
12621260
Err(CompilerError::TopLevelNonSafe)
12631261
);
12641262

12651263
assert_eq!(
1266-
policy_compile_lift_check("and(pk(),or(after(9),after(9)))"),
1264+
policy_compile_lift_check("and(pk(A),or(after(9),after(9)))"),
12671265
Err(CompilerError::ImpossibleNonMalleableCompilation)
12681266
);
12691267
}
12701268

12711269
#[test]
12721270
fn compile_q() {
1273-
let policy = SPolicy::from_str("or(1@and(pk(),pk()),127@pk())").expect("parsing");
1271+
let policy = SPolicy::from_str("or(1@and(pk(A),pk(B)),127@pk(C))").expect("parsing");
12741272
let compilation: DummySegwitAstElemExt =
12751273
best_t(&mut BTreeMap::new(), &policy, 1.0, None).unwrap();
12761274

@@ -1281,7 +1279,7 @@ mod tests {
12811279
);
12821280

12831281
let policy = SPolicy::from_str(
1284-
"and(and(and(or(127@thresh(2,pk(),pk(),thresh(2,or(127@pk(),1@pk()),after(100),or(and(pk(),after(200)),and(pk(),sha256(66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925))),pk())),1@pk()),sha256(66687aadf862bd776c8fc18b8e9f8e20089714856ee233b3902a591d0d5f2925)),or(127@pk(),1@after(300))),or(127@after(400),pk()))"
1282+
"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)))"
12851283
).expect("parsing");
12861284
let compilation: DummySegwitAstElemExt =
12871285
best_t(&mut BTreeMap::new(), &policy, 1.0, None).unwrap();
@@ -1478,42 +1476,41 @@ mod benches {
14781476

14791477
use super::{CompilerError, Concrete};
14801478
use miniscript::Segwitv0;
1481-
use DummyKey;
14821479
use Miniscript;
1483-
type DummySegwitMiniscriptRes = Result<Miniscript<DummyKey, Segwitv0>, CompilerError>;
1480+
type SegwitMsRes = Result<Miniscript<String, Segwitv0>, CompilerError>;
14841481
#[bench]
14851482
pub fn compile_basic(bh: &mut Bencher) {
14861483
let h = (0..64).map(|_| "a").collect::<String>();
1487-
let pol = Concrete::<DummyKey>::from_str(&format!(
1488-
"and(thresh(2,and(sha256({}),or(sha256({}),pk())),pk(),pk(),pk(),sha256({})),pk())",
1484+
let pol = Concrete::<String>::from_str(&format!(
1485+
"and(thresh(2,and(sha256({}),or(sha256({}),pk(A))),pk(B),pk(C),pk(D),sha256({})),pk(E))",
14891486
h, h, h
14901487
))
14911488
.expect("parsing");
14921489
bh.iter(|| {
1493-
let pt: DummySegwitMiniscriptRes = pol.compile();
1490+
let pt: SegwitMsRes = pol.compile();
14941491
black_box(pt).unwrap();
14951492
});
14961493
}
14971494

14981495
#[bench]
14991496
pub fn compile_large(bh: &mut Bencher) {
15001497
let h = (0..64).map(|_| "a").collect::<String>();
1501-
let pol = Concrete::<DummyKey>::from_str(
1502-
&format!("or(pk(),thresh(9,sha256({}),pk(),pk(),and(or(pk(),pk()),pk()),after(100),pk(),pk(),pk(),pk(),and(pk(),pk())))", h)
1498+
let pol = Concrete::<String>::from_str(
1499+
&format!("or(pk(L),thresh(9,sha256({}),pk(A),pk(B),and(or(pk(C),pk(D)),pk(E)),after(100),pk(F),pk(G),pk(H),pk(I),and(pk(J),pk(K))))", h)
15031500
).expect("parsing");
15041501
bh.iter(|| {
1505-
let pt: DummySegwitMiniscriptRes = pol.compile();
1502+
let pt: SegwitMsRes = pol.compile();
15061503
black_box(pt).unwrap();
15071504
});
15081505
}
15091506

15101507
#[bench]
15111508
pub fn compile_xlarge(bh: &mut Bencher) {
1512-
let pol = Concrete::<DummyKey>::from_str(
1513-
"or(pk(),thresh(4,pk(),older(100),pk(),and(after(100),or(pk(),or(pk(),and(pk(),thresh(2,pk(),or(pk(),and(thresh(5,pk(),or(pk(),pk()),pk(),pk(),pk(),pk(),pk(),pk(),pk(),pk(),pk()),pk())),pk(),or(and(pk(),pk()),pk()),after(100)))))),pk()))"
1509+
let pol = Concrete::<String>::from_str(
1510+
"or(pk(A),thresh(4,pk(B),older(100),pk(C),and(after(100),or(pk(D),or(pk(E),and(pk(F),thresh(2,pk(G),or(pk(H),and(thresh(5,pk(I),or(pk(J),pk(K)),pk(L),pk(M),pk(N),pk(O),pk(P),pk(Q),pk(R),pk(S),pk(T)),pk(U))),pk(V),or(and(pk(W),pk(X)),pk(Y)),after(100)))))),pk(Z)))"
15141511
).expect("parsing");
15151512
bh.iter(|| {
1516-
let pt: DummySegwitMiniscriptRes = pol.compile();
1513+
let pt: SegwitMsRes = pol.compile();
15171514
black_box(pt).unwrap();
15181515
});
15191516
}

src/policy/concrete.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
1818
use bitcoin::hashes::hex::FromHex;
1919
use bitcoin::hashes::{hash160, ripemd160, sha256, sha256d};
20+
use std::collections::HashSet;
2021
use std::{error, fmt, str};
2122

2223
use super::ENTAILMENT_MAX_TERMINALS;
@@ -88,6 +89,8 @@ pub enum PolicyError {
8889
/// lifting error: Cannot lift policies that have
8990
/// a combination of height and timelocks.
9091
HeightTimeLockCombination,
92+
/// Duplicate Public Keys
93+
DuplicatePubKeys,
9194
}
9295

9396
impl error::Error for PolicyError {
@@ -127,6 +130,7 @@ impl fmt::Display for PolicyError {
127130
PolicyError::HeightTimeLockCombination => {
128131
f.write_str("Cannot lift policies that have a heightlock and timelock combination")
129132
}
133+
PolicyError::DuplicatePubKeys => f.write_str("Policy contains duplicate keys"),
130134
}
131135
}
132136
}
@@ -182,6 +186,43 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
182186
}
183187
}
184188

189+
/// Get all keys in the policy
190+
pub fn keys(&self) -> Vec<&Pk> {
191+
match *self {
192+
Policy::Key(ref pk) => vec![pk],
193+
Policy::Threshold(_k, ref subs) => subs
194+
.iter()
195+
.map(|sub| sub.keys())
196+
.flatten()
197+
.collect::<Vec<_>>(),
198+
Policy::And(ref subs) => subs
199+
.iter()
200+
.map(|sub| sub.keys())
201+
.flatten()
202+
.collect::<Vec<_>>(),
203+
Policy::Or(ref subs) => subs
204+
.iter()
205+
.map(|(ref _k, ref sub)| sub.keys())
206+
.flatten()
207+
.collect::<Vec<_>>(),
208+
// map all hashes and time
209+
_ => vec![],
210+
}
211+
}
212+
213+
/// Check whether the policy contains duplicate public keys
214+
pub fn check_duplicate_keys(&self) -> Result<(), PolicyError> {
215+
let pks = self.keys();
216+
let pks_len = pks.len();
217+
let unique_pks_len = pks.into_iter().collect::<HashSet<_>>().len();
218+
219+
if pks_len > unique_pks_len {
220+
Err(PolicyError::DuplicatePubKeys)
221+
} else {
222+
Ok(())
223+
}
224+
}
225+
185226
/// Checks whether the given concrete policy contains a combination of
186227
/// timelocks and heightlocks.
187228
/// Returns an error if there is atleast one satisfaction that contains
@@ -244,6 +285,7 @@ impl<Pk: MiniscriptKey> Policy<Pk> {
244285
/// combination of timelocks and heightlocks
245286
pub fn is_valid(&self) -> Result<(), PolicyError> {
246287
self.check_timelocks()?;
288+
self.check_duplicate_keys()?;
247289
match *self {
248290
Policy::And(ref subs) => {
249291
if subs.len() != 2 {

0 commit comments

Comments
 (0)