Skip to content

Commit 20677e8

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 798a0a2 commit 20677e8

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,11 +1177,9 @@ mod tests {
11771177
use policy::Liftable;
11781178
use script_num_size;
11791179
use BitcoinSig;
1180-
use DummyKey;
11811180
use NullCtx;
11821181

11831182
type SPolicy = Concrete<String>;
1184-
type DummyPolicy = Concrete<DummyKey>;
11851183
type BPolicy = Concrete<bitcoin::PublicKey>;
11861184
type DummySegwitAstElemExt = policy::compiler::AstElemExt<String, Segwitv0>;
11871185
type SegwitMiniScript = Miniscript<bitcoin::PublicKey, Segwitv0>;
@@ -1212,8 +1210,8 @@ mod tests {
12121210
}
12131211

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

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

12341232
// This should compile
1235-
let pol: DummyPolicy =
1236-
DummyPolicy::from_str("and(pk(),or(and(after(9),pk()),and(after(1000000000),pk())))")
1233+
let pol: SPolicy =
1234+
SPolicy::from_str("and(pk(A),or(and(after(9),pk(B)),and(after(1000000000),pk(C))))")
12371235
.unwrap();
12381236
assert!(pol.compile::<Segwitv0>().is_ok());
12391237
}
12401238
#[test]
12411239
fn compile_basic() {
1242-
assert!(policy_compile_lift_check("pk()").is_ok());
1240+
assert!(policy_compile_lift_check("pk(A)").is_ok());
12431241
assert_eq!(
12441242
policy_compile_lift_check("after(9)"),
12451243
Err(CompilerError::TopLevelNonSafe)
@@ -1254,24 +1252,24 @@ mod tests {
12541252
),
12551253
Err(CompilerError::TopLevelNonSafe)
12561254
);
1257-
assert!(policy_compile_lift_check("and(pk(),pk())").is_ok());
1258-
assert!(policy_compile_lift_check("or(pk(),pk())").is_ok());
1259-
assert!(policy_compile_lift_check("thresh(2,pk(),pk(),pk())").is_ok());
1255+
assert!(policy_compile_lift_check("and(pk(A),pk(B))").is_ok());
1256+
assert!(policy_compile_lift_check("or(pk(A),pk(B))").is_ok());
1257+
assert!(policy_compile_lift_check("thresh(2,pk(A),pk(B),pk(C))").is_ok());
12601258

12611259
assert_eq!(
1262-
policy_compile_lift_check("thresh(2,after(9),after(9),pk())"),
1260+
policy_compile_lift_check("thresh(2,after(9),after(9),pk(A))"),
12631261
Err(CompilerError::TopLevelNonSafe)
12641262
);
12651263

12661264
assert_eq!(
1267-
policy_compile_lift_check("and(pk(),or(after(9),after(9)))"),
1265+
policy_compile_lift_check("and(pk(A),or(after(9),after(9)))"),
12681266
Err(CompilerError::ImpossibleNonMalleableCompilation)
12691267
);
12701268
}
12711269

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

@@ -1282,7 +1280,7 @@ mod tests {
12821280
);
12831281

12841282
let policy = SPolicy::from_str(
1285-
"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()))"
1283+
"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)))"
12861284
).expect("parsing");
12871285
let compilation: DummySegwitAstElemExt =
12881286
best_t(&mut BTreeMap::new(), &policy, 1.0, None).unwrap();
@@ -1488,42 +1486,41 @@ mod benches {
14881486

14891487
use super::{CompilerError, Concrete};
14901488
use miniscript::Segwitv0;
1491-
use DummyKey;
14921489
use Miniscript;
1493-
type DummySegwitMiniscriptRes = Result<Miniscript<DummyKey, Segwitv0>, CompilerError>;
1490+
type SegwitMsRes = Result<Miniscript<String, Segwitv0>, CompilerError>;
14941491
#[bench]
14951492
pub fn compile_basic(bh: &mut Bencher) {
14961493
let h = (0..64).map(|_| "a").collect::<String>();
1497-
let pol = Concrete::<DummyKey>::from_str(&format!(
1498-
"and(thresh(2,and(sha256({}),or(sha256({}),pk())),pk(),pk(),pk(),sha256({})),pk())",
1494+
let pol = Concrete::<String>::from_str(&format!(
1495+
"and(thresh(2,and(sha256({}),or(sha256({}),pk(A))),pk(B),pk(C),pk(D),sha256({})),pk(E))",
14991496
h, h, h
15001497
))
15011498
.expect("parsing");
15021499
bh.iter(|| {
1503-
let pt: DummySegwitMiniscriptRes = pol.compile();
1500+
let pt: SegwitMsRes = pol.compile();
15041501
black_box(pt).unwrap();
15051502
});
15061503
}
15071504

15081505
#[bench]
15091506
pub fn compile_large(bh: &mut Bencher) {
15101507
let h = (0..64).map(|_| "a").collect::<String>();
1511-
let pol = Concrete::<DummyKey>::from_str(
1512-
&format!("or(pk(),thresh(9,sha256({}),pk(),pk(),and(or(pk(),pk()),pk()),after(100),pk(),pk(),pk(),pk(),and(pk(),pk())))", h)
1508+
let pol = Concrete::<String>::from_str(
1509+
&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)
15131510
).expect("parsing");
15141511
bh.iter(|| {
1515-
let pt: DummySegwitMiniscriptRes = pol.compile();
1512+
let pt: SegwitMsRes = pol.compile();
15161513
black_box(pt).unwrap();
15171514
});
15181515
}
15191516

15201517
#[bench]
15211518
pub fn compile_xlarge(bh: &mut Bencher) {
1522-
let pol = Concrete::<DummyKey>::from_str(
1523-
"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()))"
1519+
let pol = Concrete::<String>::from_str(
1520+
"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)))"
15241521
).expect("parsing");
15251522
bh.iter(|| {
1526-
let pt: DummySegwitMiniscriptRes = pol.compile();
1523+
let pt: SegwitMsRes = pol.compile();
15271524
black_box(pt).unwrap();
15281525
});
15291526
}

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)