Skip to content

Commit 4d2b4b0

Browse files
committed
Change After and Older to check timestamps as well
1 parent 5e631e3 commit 4d2b4b0

File tree

4 files changed

+140
-18
lines changed

4 files changed

+140
-18
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88
- Change trait `GetBlockHash` to `GetBlockInfo`. A new function is added to the new trait `get_block_header` which expects a block height and returns corresponding block header. This is implemented on every blockchain backend.
9+
- Amend the struct `After` and `Older` to accept and check against timestamps as well.
910

1011
## [v0.20.0] - [v0.19.0]
1112

src/descriptor/policy.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -904,7 +904,7 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
904904
..
905905
} = build_sat
906906
{
907-
let after = After::new(Some(current_height), false);
907+
let after = After::new(Some(current_height), None, false);
908908
let after_sat = Satisfier::<bitcoin::PublicKey>::check_after(&after, *value);
909909
let inputs_sat = psbt_inputs_sat(psbt)
910910
.all(|sat| Satisfier::<bitcoin::PublicKey>::check_after(&sat, *value));
@@ -929,7 +929,13 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
929929
psbt,
930930
} = build_sat
931931
{
932-
let older = Older::new(Some(current_height), Some(input_max_height), false);
932+
let older = Older::new(
933+
Some(current_height),
934+
None,
935+
Some(input_max_height),
936+
None,
937+
false,
938+
);
933939
let older_sat = Satisfier::<bitcoin::PublicKey>::check_older(&older, *value);
934940
let inputs_sat = psbt_inputs_sat(psbt)
935941
.all(|sat| Satisfier::<bitcoin::PublicKey>::check_older(&sat, *value));

src/wallet/mod.rs

+93-4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use std::cell::RefCell;
1717
use std::collections::HashMap;
1818
use std::collections::{BTreeMap, HashSet};
19+
use std::convert::TryInto;
1920
use std::fmt;
2021
use std::ops::{Deref, DerefMut};
2122
use std::str::FromStr;
@@ -1148,15 +1149,37 @@ where
11481149
.borrow()
11491150
.get_tx(&input.previous_output.txid, false)?
11501151
.map(|tx| tx.confirmation_time.map(|c| c.height).unwrap_or(u32::MAX));
1152+
let create_time = self
1153+
.database
1154+
.borrow()
1155+
.get_tx(&input.previous_output.txid, false)?
1156+
.map(|tx| {
1157+
tx.confirmation_time
1158+
.map(|c| {
1159+
c.timestamp
1160+
.try_into()
1161+
.expect("Time is greater than 0xFFFFFFFF")
1162+
})
1163+
.unwrap_or(u32::MAX)
1164+
});
11511165
let last_sync_height = self
11521166
.database()
11531167
.get_sync_time()?
11541168
.map(|sync_time| sync_time.block_time.height);
11551169
let current_height = sign_options.assume_height.or(last_sync_height);
1170+
// TODO: Change current time to median time of latest 11 blocks with Blockchain::GetBlockInfo
1171+
// according to BIP-113
1172+
let current_time = self.database().get_sync_time()?.map(|sync_time| {
1173+
sync_time
1174+
.block_time
1175+
.timestamp
1176+
.try_into()
1177+
.expect("Time is greater than 0xFFFFFFFF")
1178+
});
11561179

11571180
debug!(
1158-
"Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?}",
1159-
n, input.previous_output, create_height, current_height
1181+
"Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?}, `create_time` = {:?}, `current_time` = {:?}",
1182+
n, input.previous_output, create_height, current_height, create_time, current_time
11601183
);
11611184

11621185
// - Try to derive the descriptor by looking at the txout. If it's in our database, we
@@ -1190,8 +1213,16 @@ where
11901213
&mut tmp_input,
11911214
(
11921215
PsbtInputSatisfier::new(psbt, n),
1193-
After::new(current_height, false),
1194-
Older::new(current_height, create_height, false),
1216+
// FIXME: The satisfier doesn't call check methods of After and Older defined in wallet/utils.rs
1217+
// Instead it calls the implementations defined in miniscript
1218+
After::new(current_height, current_time, false),
1219+
Older::new(
1220+
current_height,
1221+
current_time,
1222+
create_height,
1223+
create_time,
1224+
false,
1225+
),
11951226
),
11961227
) {
11971228
Ok(_) => {
@@ -1913,11 +1944,21 @@ pub(crate) mod test {
19131944
"wsh(or_d(pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu),and_v(v:pk(cMnkdebixpXMPfkcNEjjGin7s94hiehAH4mLbYkZoh9KSiNNmqC8),older(144))))"
19141945
}
19151946

1947+
pub(crate) fn get_test_single_sig_csv_with_time() -> &'static str {
1948+
// and(pk(Alice),older(4194904)) // (1 << 22) | 600 -> lock of 600 seconds with type time
1949+
"wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(4194904)))"
1950+
}
1951+
19161952
pub(crate) fn get_test_single_sig_cltv() -> &'static str {
19171953
// and(pk(Alice),after(100000))
19181954
"wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(100000)))"
19191955
}
19201956

1957+
pub(crate) fn get_test_single_sig_cltv_with_future_time() -> &'static str {
1958+
// and(pk(Alice),after(1893456000)) // Tue Jan 01 2030 00:00:00 GMT+0000
1959+
"wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(1893456000)))"
1960+
}
1961+
19211962
pub(crate) fn get_test_tr_single_sig() -> &'static str {
19221963
"tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG)"
19231964
}
@@ -2108,6 +2149,54 @@ pub(crate) mod test {
21082149
assert_eq!(psbt.unsigned_tx.lock_time, 100_000);
21092150
}
21102151

2152+
#[test]
2153+
fn test_create_tx_locktime_cltv_with_time() {
2154+
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv_with_future_time());
2155+
let addr = wallet.get_address(New).unwrap();
2156+
let mut builder = wallet.build_tx();
2157+
builder.add_recipient(addr.script_pubkey(), 25_000);
2158+
let (psbt, _) = builder.finish().unwrap();
2159+
let after = After::new(None, Some(time::get_timestamp() as u32), false);
2160+
let after_sat = miniscript::Satisfier::<bitcoin::PublicKey>::check_after(
2161+
&after,
2162+
psbt.unsigned_tx.lock_time,
2163+
);
2164+
2165+
assert!(!after_sat);
2166+
2167+
let after = After::new(None, Some(1893456000), false);
2168+
let after_sat = miniscript::Satisfier::<bitcoin::PublicKey>::check_after(
2169+
&after,
2170+
psbt.unsigned_tx.lock_time,
2171+
);
2172+
assert!(after_sat);
2173+
}
2174+
2175+
#[test]
2176+
fn test_create_tx_locktime_csv_with_time() {
2177+
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv_with_time());
2178+
let addr = wallet.get_address(New).unwrap();
2179+
let mut builder = wallet.build_tx();
2180+
builder.add_recipient(addr.script_pubkey(), 25_000);
2181+
let (psbt, _) = builder.finish().unwrap();
2182+
let time_stamp = Some(time::get_timestamp() as u32);
2183+
let late_time_stamp = Some(time::get_timestamp() as u32 + 601);
2184+
2185+
let older = Older::new(None, time_stamp, None, time_stamp, false);
2186+
let older_sat = miniscript::Satisfier::<bitcoin::PublicKey>::check_older(
2187+
&older,
2188+
psbt.unsigned_tx.input[0].sequence,
2189+
);
2190+
assert!(!older_sat);
2191+
2192+
let older = Older::new(None, late_time_stamp, None, time_stamp, false);
2193+
let older_sat = miniscript::Satisfier::<bitcoin::PublicKey>::check_older(
2194+
&older,
2195+
psbt.unsigned_tx.input[0].sequence,
2196+
);
2197+
assert!(older_sat);
2198+
}
2199+
21112200
#[test]
21122201
fn test_create_tx_custom_locktime() {
21132202
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());

src/wallet/utils.rs

+38-12
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,20 @@ impl IsDust for u64 {
4444

4545
pub struct After {
4646
pub current_height: Option<u32>,
47-
pub assume_height_reached: bool,
47+
pub current_time: Option<u32>,
48+
pub assume_reached: bool,
4849
}
4950

5051
impl After {
51-
pub(crate) fn new(current_height: Option<u32>, assume_height_reached: bool) -> After {
52+
pub(crate) fn new(
53+
current_height: Option<u32>,
54+
current_time: Option<u32>,
55+
assume_reached: bool,
56+
) -> After {
5257
After {
5358
current_height,
54-
assume_height_reached,
59+
current_time,
60+
assume_reached,
5561
}
5662
}
5763
}
@@ -96,41 +102,61 @@ pub(crate) fn check_nlocktime(nlocktime: u32, required: u32) -> bool {
96102

97103
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
98104
fn check_after(&self, n: u32) -> bool {
99-
if let Some(current_height) = self.current_height {
100-
current_height >= n
105+
if n < BLOCKS_TIMELOCK_THRESHOLD {
106+
if let Some(current_height) = self.current_height {
107+
current_height >= n
108+
} else {
109+
self.assume_reached
110+
}
111+
} else if let Some(current_time) = self.current_time {
112+
current_time >= n
101113
} else {
102-
self.assume_height_reached
114+
self.assume_reached
103115
}
104116
}
105117
}
106118

107119
pub struct Older {
108120
pub current_height: Option<u32>,
121+
pub current_time: Option<u32>,
109122
pub create_height: Option<u32>,
110-
pub assume_height_reached: bool,
123+
pub create_time: Option<u32>,
124+
pub assume_reached: bool,
111125
}
112126

113127
impl Older {
114128
pub(crate) fn new(
115129
current_height: Option<u32>,
130+
current_time: Option<u32>,
116131
create_height: Option<u32>,
117-
assume_height_reached: bool,
132+
create_time: Option<u32>,
133+
assume_reached: bool,
118134
) -> Older {
119135
Older {
120136
current_height,
137+
current_time,
121138
create_height,
122-
assume_height_reached,
139+
create_time,
140+
assume_reached,
123141
}
124142
}
125143
}
126144

127145
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
128146
fn check_older(&self, n: u32) -> bool {
129-
if let Some(current_height) = self.current_height {
147+
let masked_n = n & SEQUENCE_LOCKTIME_MASK;
148+
if n & SEQUENCE_LOCKTIME_TYPE_FLAG == 0 {
149+
if let Some(current_height) = self.current_height {
150+
// TODO: test >= / >
151+
current_height as u64 >= self.create_height.unwrap_or(0) as u64 + masked_n as u64
152+
} else {
153+
self.assume_reached
154+
}
155+
} else if let Some(current_time) = self.current_time {
130156
// TODO: test >= / >
131-
current_height as u64 >= self.create_height.unwrap_or(0) as u64 + n as u64
157+
current_time as u64 >= self.create_time.unwrap_or(0) as u64 + masked_n as u64
132158
} else {
133-
self.assume_height_reached
159+
self.assume_reached
134160
}
135161
}
136162
}

0 commit comments

Comments
 (0)