Skip to content

Commit 9098986

Browse files
committed
Added Timelock info
1 parent 3ce5ccc commit 9098986

File tree

1 file changed

+149
-1
lines changed

1 file changed

+149
-1
lines changed

src/miniscript/types/extra_props.rs

Lines changed: 149 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,94 @@
44
use super::{Error, ErrorKind, Property, ScriptContext};
55
use script_num_size;
66
use std::cmp;
7+
use std::iter::once;
78
use MiniscriptKey;
89
use Terminal;
910

1011
pub const MAX_OPS_PER_SCRIPT: usize = 201;
12+
// https://github.com/bitcoin/bitcoin/blob/9ccaee1d5e2e4b79b0a7c29aadb41b97e4741332/src/script/script.h#L39
13+
pub const HEIGHT_TIME_THRESHOLD: u32 = 500_000_000;
14+
15+
/// Helper struct Whether any satisfaction of this fragment contains any timelocks
16+
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
17+
pub struct TimeLockInfo {
18+
// csv with heights
19+
pub csv_with_height: bool,
20+
/// csv with times
21+
pub csv_with_time: bool,
22+
/// cltv with heights
23+
pub cltv_with_height: bool,
24+
/// cltv with times
25+
pub cltv_with_time: bool,
26+
/// combination of any heightlocks and timelocks
27+
pub contains_combination: bool,
28+
}
29+
30+
impl Default for TimeLockInfo {
31+
fn default() -> Self {
32+
Self {
33+
csv_with_height: false,
34+
csv_with_time: false,
35+
cltv_with_height: false,
36+
cltv_with_time: false,
37+
contains_combination: false,
38+
}
39+
}
40+
}
41+
42+
impl TimeLockInfo {
43+
/// Whether the current contains any possible unspendable
44+
/// path
45+
pub fn contains_unspendable_path(self) -> bool {
46+
self.contains_combination
47+
}
48+
49+
// handy function for combining `and` timelocks
50+
// This can be operator overloaded in future
51+
pub(crate) fn comb_and_timelocks(a: Self, b: Self) -> Self {
52+
Self::combine_thresh_timelocks(2, once(a).chain(once(b)))
53+
}
54+
55+
// handy function for combining `or` timelocks
56+
// This can be operator overloaded in future
57+
pub(crate) fn comb_or_timelocks(a: Self, b: Self) -> Self {
58+
Self::combine_thresh_timelocks(1, once(a).chain(once(b)))
59+
}
60+
61+
pub(crate) fn combine_thresh_timelocks<I>(k: usize, sub_timelocks: I) -> TimeLockInfo
62+
where
63+
I: IntoIterator<Item = TimeLockInfo>,
64+
{
65+
// timelocks calculation
66+
// Propagate all fields of `TimelockInfo` from each of the node's children to the node
67+
// itself (by taking the logical-or of all of them). In case `k == 1` (this is a disjunction)
68+
// this is all we need to do: the node may behave like any of its children, for purposes
69+
// of timelock accounting.
70+
//
71+
// If `k > 1` we have the additional consideration that if any two children have conflicting
72+
// timelock requirements, this represents an inaccessible spending branch.
73+
sub_timelocks.into_iter().fold(
74+
TimeLockInfo::default(),
75+
|mut timelock_info, sub_timelock| {
76+
// If more than one branch may be taken, and some other branch has a requirement
77+
// that conflicts with this one, set `contains_combination`
78+
if k >= 2 {
79+
timelock_info.contains_combination |= (timelock_info.csv_with_height
80+
&& sub_timelock.csv_with_time)
81+
|| (timelock_info.csv_with_time && sub_timelock.csv_with_height)
82+
|| (timelock_info.cltv_with_time && sub_timelock.cltv_with_height)
83+
|| (timelock_info.cltv_with_height && sub_timelock.cltv_with_time);
84+
}
85+
timelock_info.csv_with_height |= sub_timelock.csv_with_height;
86+
timelock_info.csv_with_time |= sub_timelock.csv_with_time;
87+
timelock_info.cltv_with_height |= sub_timelock.cltv_with_height;
88+
timelock_info.cltv_with_time |= sub_timelock.cltv_with_time;
89+
timelock_info.contains_combination |= sub_timelock.contains_combination;
90+
timelock_info
91+
},
92+
)
93+
}
94+
}
1195

1296
/// Structure representing the extra type properties of a fragment which are
1397
/// relevant to legacy(pre-segwit) safety and fee estimation. If a fragment is
@@ -25,6 +109,8 @@ pub struct ExtData {
25109
pub ops_count_sat: Option<usize>,
26110
/// The worst case ops-count for dissatisfying this Miniscript fragment.
27111
pub ops_count_nsat: Option<usize>,
112+
/// The timelock info about heightlocks and timelocks
113+
pub timelock_info: TimeLockInfo,
28114
}
29115

30116
impl Property for ExtData {
@@ -39,6 +125,7 @@ impl Property for ExtData {
39125
ops_count_static: 0,
40126
ops_count_sat: Some(0),
41127
ops_count_nsat: None,
128+
timelock_info: TimeLockInfo::default(),
42129
}
43130
}
44131

@@ -49,6 +136,7 @@ impl Property for ExtData {
49136
ops_count_static: 0,
50137
ops_count_sat: None,
51138
ops_count_nsat: Some(0),
139+
timelock_info: TimeLockInfo::default(),
52140
}
53141
}
54142

@@ -59,6 +147,7 @@ impl Property for ExtData {
59147
ops_count_static: 0,
60148
ops_count_sat: Some(0),
61149
ops_count_nsat: Some(0),
150+
timelock_info: TimeLockInfo::default(),
62151
}
63152
}
64153

@@ -69,6 +158,7 @@ impl Property for ExtData {
69158
ops_count_static: 3,
70159
ops_count_sat: Some(3),
71160
ops_count_nsat: Some(3),
161+
timelock_info: TimeLockInfo::default(),
72162
}
73163
}
74164

@@ -85,6 +175,7 @@ impl Property for ExtData {
85175
ops_count_static: 1,
86176
ops_count_sat: Some(n + 1),
87177
ops_count_nsat: Some(n + 1),
178+
timelock_info: TimeLockInfo::default(),
88179
}
89180
}
90181

@@ -100,6 +191,7 @@ impl Property for ExtData {
100191
ops_count_static: 4,
101192
ops_count_sat: Some(4),
102193
ops_count_nsat: None,
194+
timelock_info: TimeLockInfo::default(),
103195
}
104196
}
105197

@@ -110,6 +202,7 @@ impl Property for ExtData {
110202
ops_count_static: 4,
111203
ops_count_sat: Some(4),
112204
ops_count_nsat: None,
205+
timelock_info: TimeLockInfo::default(),
113206
}
114207
}
115208

@@ -120,6 +213,7 @@ impl Property for ExtData {
120213
ops_count_static: 4,
121214
ops_count_sat: Some(4),
122215
ops_count_nsat: None,
216+
timelock_info: TimeLockInfo::default(),
123217
}
124218
}
125219

@@ -130,25 +224,56 @@ impl Property for ExtData {
130224
ops_count_static: 4,
131225
ops_count_sat: Some(4),
132226
ops_count_nsat: None,
227+
timelock_info: TimeLockInfo::default(),
228+
}
229+
}
230+
231+
fn from_time(_t: u32) -> Self {
232+
unreachable!()
233+
}
234+
235+
fn from_after(t: u32) -> Self {
236+
ExtData {
237+
pk_cost: script_num_size(t as usize) + 1,
238+
has_free_verify: false,
239+
ops_count_static: 1,
240+
ops_count_sat: Some(1),
241+
ops_count_nsat: None,
242+
timelock_info: TimeLockInfo {
243+
csv_with_height: false,
244+
csv_with_time: false,
245+
cltv_with_height: t < HEIGHT_TIME_THRESHOLD,
246+
cltv_with_time: t >= HEIGHT_TIME_THRESHOLD,
247+
contains_combination: false,
248+
},
133249
}
134250
}
135251

136-
fn from_time(t: u32) -> Self {
252+
fn from_older(t: u32) -> Self {
137253
ExtData {
138254
pk_cost: script_num_size(t as usize) + 1,
139255
has_free_verify: false,
140256
ops_count_static: 1,
141257
ops_count_sat: Some(1),
142258
ops_count_nsat: None,
259+
timelock_info: TimeLockInfo {
260+
csv_with_height: t < HEIGHT_TIME_THRESHOLD,
261+
csv_with_time: t >= HEIGHT_TIME_THRESHOLD,
262+
cltv_with_height: false,
263+
cltv_with_time: false,
264+
contains_combination: false,
265+
},
143266
}
144267
}
268+
145269
fn cast_alt(self) -> Result<Self, ErrorKind> {
146270
Ok(ExtData {
147271
pk_cost: self.pk_cost + 2,
148272
has_free_verify: false,
149273
ops_count_static: self.ops_count_static + 2,
150274
ops_count_sat: self.ops_count_sat.map(|x| x + 2),
151275
ops_count_nsat: self.ops_count_nsat.map(|x| x + 2),
276+
timelock_info: self.timelock_info,
152277
})
153278
}
154279

@@ -159,6 +284,7 @@ impl Property for ExtData {
159284
ops_count_static: self.ops_count_static + 1,
160285
ops_count_sat: self.ops_count_sat.map(|x| x + 1),
161286
ops_count_nsat: self.ops_count_nsat.map(|x| x + 1),
287+
timelock_info: self.timelock_info,
162288
})
163289
}
164290

@@ -169,6 +295,7 @@ impl Property for ExtData {
169295
ops_count_static: self.ops_count_static + 1,
170296
ops_count_sat: self.ops_count_sat.map(|x| x + 1),
171297
ops_count_nsat: self.ops_count_nsat.map(|x| x + 1),
298+
timelock_info: self.timelock_info,
172299
})
173300
}
174301

@@ -179,6 +306,7 @@ impl Property for ExtData {
179306
ops_count_static: self.ops_count_static + 3,
180307
ops_count_sat: self.ops_count_sat.map(|x| x + 3),
181308
ops_count_nsat: Some(self.ops_count_static + 3),
309+
timelock_info: self.timelock_info,
182310
})
183311
}
184312

@@ -190,6 +318,7 @@ impl Property for ExtData {
190318
ops_count_static: self.ops_count_static + verify_cost,
191319
ops_count_sat: self.ops_count_sat.map(|x| x + verify_cost),
192320
ops_count_nsat: None,
321+
timelock_info: self.timelock_info,
193322
})
194323
}
195324

@@ -200,6 +329,7 @@ impl Property for ExtData {
200329
ops_count_static: self.ops_count_static + 4,
201330
ops_count_sat: self.ops_count_sat.map(|x| x + 4),
202331
ops_count_nsat: Some(self.ops_count_static + 4),
332+
timelock_info: self.timelock_info,
203333
})
204334
}
205335

@@ -210,6 +340,7 @@ impl Property for ExtData {
210340
ops_count_static: self.ops_count_static + 1,
211341
ops_count_sat: self.ops_count_sat.map(|x| x + 1),
212342
ops_count_nsat: self.ops_count_nsat.map(|x| x + 1),
343+
timelock_info: self.timelock_info,
213344
})
214345
}
215346

@@ -220,6 +351,7 @@ impl Property for ExtData {
220351
ops_count_static: self.ops_count_static,
221352
ops_count_sat: self.ops_count_sat,
222353
ops_count_nsat: None,
354+
timelock_info: self.timelock_info,
223355
})
224356
}
225357

@@ -235,6 +367,7 @@ impl Property for ExtData {
235367
ops_count_static: self.ops_count_static + 3,
236368
ops_count_sat: self.ops_count_sat.map(|x| x + 3),
237369
ops_count_nsat: Some(self.ops_count_static + 3),
370+
timelock_info: self.timelock_info,
238371
})
239372
}
240373

@@ -245,6 +378,7 @@ impl Property for ExtData {
245378
ops_count_static: self.ops_count_static + 3,
246379
ops_count_sat: self.ops_count_sat.map(|x| x + 3),
247380
ops_count_nsat: Some(self.ops_count_static + 3),
381+
timelock_info: self.timelock_info,
248382
})
249383
}
250384

@@ -259,6 +393,7 @@ impl Property for ExtData {
259393
ops_count_nsat: l
260394
.ops_count_nsat
261395
.and_then(|x| r.ops_count_nsat.map(|y| x + y + 1)),
396+
timelock_info: TimeLockInfo::comb_and_timelocks(l.timelock_info, r.timelock_info),
262397
})
263398
}
264399

@@ -269,6 +404,7 @@ impl Property for ExtData {
269404
ops_count_static: l.ops_count_static + r.ops_count_static,
270405
ops_count_sat: l.ops_count_sat.and_then(|x| r.ops_count_sat.map(|y| x + y)),
271406
ops_count_nsat: None,
407+
timelock_info: TimeLockInfo::comb_and_timelocks(l.timelock_info, r.timelock_info),
272408
})
273409
}
274410

@@ -286,6 +422,7 @@ impl Property for ExtData {
286422
ops_count_nsat: l
287423
.ops_count_nsat
288424
.and_then(|x| r.ops_count_nsat.map(|y| x + y + 1)),
425+
timelock_info: TimeLockInfo::comb_or_timelocks(l.timelock_info, r.timelock_info),
289426
})
290427
}
291428

@@ -302,6 +439,7 @@ impl Property for ExtData {
302439
ops_count_nsat: l
303440
.ops_count_nsat
304441
.and_then(|x| r.ops_count_nsat.map(|y| x + y + 3)),
442+
timelock_info: TimeLockInfo::comb_or_timelocks(l.timelock_info, r.timelock_info),
305443
})
306444
}
307445

@@ -316,6 +454,7 @@ impl Property for ExtData {
316454
.and_then(|x| l.ops_count_nsat.map(|y| y + x + 2)),
317455
),
318456
ops_count_nsat: None,
457+
timelock_info: TimeLockInfo::comb_or_timelocks(l.timelock_info, r.timelock_info),
319458
})
320459
}
321460

@@ -333,6 +472,7 @@ impl Property for ExtData {
333472
(_, Some(x)) | (Some(x), _) => Some(x + 3),
334473
(None, None) => None,
335474
},
475+
timelock_info: TimeLockInfo::comb_or_timelocks(l.timelock_info, r.timelock_info),
336476
})
337477
}
338478

@@ -350,6 +490,10 @@ impl Property for ExtData {
350490
ops_count_nsat: c
351491
.ops_count_nsat
352492
.and_then(|z| a.ops_count_nsat.map(|x| x + b.ops_count_static + z + 3)),
493+
timelock_info: TimeLockInfo::comb_or_timelocks(
494+
TimeLockInfo::comb_and_timelocks(a.timelock_info, b.timelock_info),
495+
c.timelock_info,
496+
),
353497
})
354498
}
355499

@@ -364,10 +508,13 @@ impl Property for ExtData {
364508
let mut ops_count_nsat = Some(0);
365509
let mut ops_count_sat = Some(0);
366510
let mut sat_count = 0;
511+
let mut timelocks = vec![];
367512
for i in 0..n {
368513
let sub = sub_ck(i)?;
514+
369515
pk_cost += sub.pk_cost;
370516
ops_count_static += sub.ops_count_static;
517+
timelocks.push(sub.timelock_info);
371518
match (sub.ops_count_sat, sub.ops_count_nsat) {
372519
(Some(x), Some(y)) => {
373520
ops_count_sat_vec.push(Some(x as i32 - y as i32));
@@ -402,6 +549,7 @@ impl Property for ExtData {
402549
ops_count_sat: ops_count_sat
403550
.map(|x: usize| (x + (n - 1) + 1 + (sum + ops_count_nsat_sum as i32) as usize)), //adds and equal
404551
ops_count_nsat: ops_count_nsat.map(|x| x + (n - 1) + 1), //adds and equal
552+
timelock_info: TimeLockInfo::combine_thresh_timelocks(k, timelocks),
405553
})
406554
}
407555

0 commit comments

Comments
 (0)