Skip to content

Commit

Permalink
Change After and Older to check timestamps as well
Browse files Browse the repository at this point in the history
  • Loading branch information
wszdexdrf committed Aug 2, 2022
1 parent f2634fc commit 5541f1d
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Move change creation from `Wallet::create_tx` to `CoinSelectionAlgorithm::coin_select`.
- Change the interface of `SqliteDatabase::new` to accept any type that implement AsRef<Path>
- 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.
- Amend the struct `After` and `Older` to accept and check against timestamps as well.

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

Expand Down
10 changes: 8 additions & 2 deletions src/descriptor/policy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -904,7 +904,7 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
..
} = build_sat
{
let after = After::new(Some(current_height), false);
let after = After::new(Some(current_height), None, false);
let after_sat = Satisfier::<bitcoin::PublicKey>::check_after(&after, *value);
let inputs_sat = psbt_inputs_sat(psbt)
.all(|sat| Satisfier::<bitcoin::PublicKey>::check_after(&sat, *value));
Expand All @@ -929,7 +929,13 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
psbt,
} = build_sat
{
let older = Older::new(Some(current_height), Some(input_max_height), false);
let older = Older::new(
Some(current_height),
None,
Some(input_max_height),
None,
false,
);
let older_sat = Satisfier::<bitcoin::PublicKey>::check_older(&older, *value);
let inputs_sat = psbt_inputs_sat(psbt)
.all(|sat| Satisfier::<bitcoin::PublicKey>::check_older(&sat, *value));
Expand Down
97 changes: 93 additions & 4 deletions src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::{BTreeMap, HashSet};
use std::convert::TryInto;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
Expand Down Expand Up @@ -1155,15 +1156,37 @@ where
.borrow()
.get_tx(&input.previous_output.txid, false)?
.map(|tx| tx.confirmation_time.map(|c| c.height).unwrap_or(u32::MAX));
let create_time = self
.database
.borrow()
.get_tx(&input.previous_output.txid, false)?
.map(|tx| {
tx.confirmation_time
.map(|c| {
c.timestamp
.try_into()
.expect("Time is greater than 0xFFFFFFFF")
})
.unwrap_or(u32::MAX)
});
let last_sync_height = self
.database()
.get_sync_time()?
.map(|sync_time| sync_time.block_time.height);
let current_height = sign_options.assume_height.or(last_sync_height);
// TODO: Change current time to median time of latest 11 blocks with Blockchain::GetBlockInfo
// according to BIP-113
let current_time = self.database().get_sync_time()?.map(|sync_time| {
sync_time
.block_time
.timestamp
.try_into()
.expect("Time is greater than 0xFFFFFFFF")
});

debug!(
"Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?}",
n, input.previous_output, create_height, current_height
"Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?}, `create_time` = {:?}, `current_time` = {:?}",
n, input.previous_output, create_height, current_height, create_time, current_time
);

// - Try to derive the descriptor by looking at the txout. If it's in our database, we
Expand Down Expand Up @@ -1197,8 +1220,16 @@ where
&mut tmp_input,
(
PsbtInputSatisfier::new(psbt, n),
After::new(current_height, false),
Older::new(current_height, create_height, false),
// FIXME: The satisfier doesn't call check methods of After and Older defined in wallet/utils.rs
// Instead it calls the implementations defined in miniscript
After::new(current_height, current_time, false),
Older::new(
current_height,
current_time,
create_height,
create_time,
false,
),
),
) {
Ok(_) => {
Expand Down Expand Up @@ -1962,11 +1993,21 @@ pub(crate) mod test {
"wsh(or_d(pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu),and_v(v:pk(cMnkdebixpXMPfkcNEjjGin7s94hiehAH4mLbYkZoh9KSiNNmqC8),older(144))))"
}

pub(crate) fn get_test_single_sig_csv_with_time() -> &'static str {
// and(pk(Alice),older(4194904)) // (1 << 22) | 600 -> lock of 600 seconds with type time
"wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),older(4194904)))"
}

pub(crate) fn get_test_single_sig_cltv() -> &'static str {
// and(pk(Alice),after(100000))
"wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(100000)))"
}

pub(crate) fn get_test_single_sig_cltv_with_future_time() -> &'static str {
// and(pk(Alice),after(1893456000)) // Tue Jan 01 2030 00:00:00 GMT+0000
"wsh(and_v(v:pk(cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW),after(1893456000)))"
}

pub(crate) fn get_test_tr_single_sig() -> &'static str {
"tr(cNJmN3fH9DDbDt131fQNkVakkpzawJBSeybCUNmP1BovpmGQ45xG)"
}
Expand Down Expand Up @@ -2157,6 +2198,54 @@ pub(crate) mod test {
assert_eq!(psbt.unsigned_tx.lock_time, 100_000);
}

#[test]
fn test_create_tx_locktime_cltv_with_time() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv_with_future_time());
let addr = wallet.get_address(New).unwrap();
let mut builder = wallet.build_tx();
builder.add_recipient(addr.script_pubkey(), 25_000);
let (psbt, _) = builder.finish().unwrap();
let after = After::new(None, Some(time::get_timestamp() as u32), false);
let after_sat = miniscript::Satisfier::<bitcoin::PublicKey>::check_after(
&after,
psbt.unsigned_tx.lock_time,
);

assert!(!after_sat);

let after = After::new(None, Some(1893456000), false);
let after_sat = miniscript::Satisfier::<bitcoin::PublicKey>::check_after(
&after,
psbt.unsigned_tx.lock_time,
);
assert!(after_sat);
}

#[test]
fn test_create_tx_locktime_csv_with_time() {
let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv_with_time());
let addr = wallet.get_address(New).unwrap();
let mut builder = wallet.build_tx();
builder.add_recipient(addr.script_pubkey(), 25_000);
let (psbt, _) = builder.finish().unwrap();
let time_stamp = Some(time::get_timestamp() as u32);
let late_time_stamp = Some(time::get_timestamp() as u32 + 601);

let older = Older::new(None, time_stamp, None, time_stamp, false);
let older_sat = miniscript::Satisfier::<bitcoin::PublicKey>::check_older(
&older,
psbt.unsigned_tx.input[0].sequence,
);
assert!(!older_sat);

let older = Older::new(None, late_time_stamp, None, time_stamp, false);
let older_sat = miniscript::Satisfier::<bitcoin::PublicKey>::check_older(
&older,
psbt.unsigned_tx.input[0].sequence,
);
assert!(older_sat);
}

#[test]
fn test_create_tx_custom_locktime() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
Expand Down
50 changes: 38 additions & 12 deletions src/wallet/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,20 @@ impl IsDust for u64 {

pub struct After {
pub current_height: Option<u32>,
pub assume_height_reached: bool,
pub current_time: Option<u32>,
pub assume_reached: bool,
}

impl After {
pub(crate) fn new(current_height: Option<u32>, assume_height_reached: bool) -> After {
pub(crate) fn new(
current_height: Option<u32>,
current_time: Option<u32>,
assume_reached: bool,
) -> After {
After {
current_height,
assume_height_reached,
current_time,
assume_reached,
}
}
}
Expand Down Expand Up @@ -96,41 +102,61 @@ pub(crate) fn check_nlocktime(nlocktime: u32, required: u32) -> bool {

impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
fn check_after(&self, n: u32) -> bool {
if let Some(current_height) = self.current_height {
current_height >= n
if n < BLOCKS_TIMELOCK_THRESHOLD {
if let Some(current_height) = self.current_height {
current_height >= n
} else {
self.assume_reached
}
} else if let Some(current_time) = self.current_time {
current_time >= n
} else {
self.assume_height_reached
self.assume_reached
}
}
}

pub struct Older {
pub current_height: Option<u32>,
pub current_time: Option<u32>,
pub create_height: Option<u32>,
pub assume_height_reached: bool,
pub create_time: Option<u32>,
pub assume_reached: bool,
}

impl Older {
pub(crate) fn new(
current_height: Option<u32>,
current_time: Option<u32>,
create_height: Option<u32>,
assume_height_reached: bool,
create_time: Option<u32>,
assume_reached: bool,
) -> Older {
Older {
current_height,
current_time,
create_height,
assume_height_reached,
create_time,
assume_reached,
}
}
}

impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
fn check_older(&self, n: u32) -> bool {
if let Some(current_height) = self.current_height {
let masked_n = n & SEQUENCE_LOCKTIME_MASK;
if n & SEQUENCE_LOCKTIME_TYPE_FLAG == 0 {
if let Some(current_height) = self.current_height {
// TODO: test >= / >
current_height as u64 >= self.create_height.unwrap_or(0) as u64 + masked_n as u64
} else {
self.assume_reached
}
} else if let Some(current_time) = self.current_time {
// TODO: test >= / >
current_height as u64 >= self.create_height.unwrap_or(0) as u64 + n as u64
current_time as u64 >= self.create_time.unwrap_or(0) as u64 + masked_n as u64
} else {
self.assume_height_reached
self.assume_reached
}
}
}
Expand Down

0 comments on commit 5541f1d

Please sign in to comment.