2023-03-01 11:09:08 +01:00
|
|
|
#![cfg(feature = "miniscript")]
|
|
|
|
|
|
|
|
#[macro_use]
|
|
|
|
mod common;
|
|
|
|
use bdk_chain::{
|
|
|
|
collections::BTreeMap,
|
2023-08-25 12:52:09 +03:00
|
|
|
indexed_tx_graph::Indexer,
|
2023-10-12 00:03:18 +08:00
|
|
|
keychain::{self, ChangeSet, KeychainTxOutIndex},
|
2023-07-14 17:52:08 +03:00
|
|
|
Append,
|
2023-03-01 11:09:08 +01:00
|
|
|
};
|
|
|
|
|
2023-10-16 19:51:53 +11:00
|
|
|
use bitcoin::{secp256k1::Secp256k1, Amount, OutPoint, ScriptBuf, Transaction, TxOut};
|
2023-03-01 11:09:08 +01:00
|
|
|
use miniscript::{Descriptor, DescriptorPublicKey};
|
|
|
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
|
|
|
|
enum TestKeychain {
|
|
|
|
External,
|
|
|
|
Internal,
|
|
|
|
}
|
|
|
|
|
2023-11-28 18:08:49 +01:00
|
|
|
fn init_txout_index(
|
|
|
|
lookahead: u32,
|
|
|
|
) -> (
|
2023-03-01 11:09:08 +01:00
|
|
|
bdk_chain::keychain::KeychainTxOutIndex<TestKeychain>,
|
|
|
|
Descriptor<DescriptorPublicKey>,
|
|
|
|
Descriptor<DescriptorPublicKey>,
|
|
|
|
) {
|
2023-11-28 18:08:49 +01:00
|
|
|
let mut txout_index = bdk_chain::keychain::KeychainTxOutIndex::<TestKeychain>::new(lookahead);
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
|
|
|
|
let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
|
|
|
|
let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
|
|
|
|
|
|
|
|
txout_index.add_keychain(TestKeychain::External, external_descriptor.clone());
|
|
|
|
txout_index.add_keychain(TestKeychain::Internal, internal_descriptor.clone());
|
|
|
|
|
|
|
|
(txout_index, external_descriptor, internal_descriptor)
|
|
|
|
}
|
|
|
|
|
2023-06-21 17:59:34 +02:00
|
|
|
fn spk_at_index(descriptor: &Descriptor<DescriptorPublicKey>, index: u32) -> ScriptBuf {
|
2023-03-01 11:09:08 +01:00
|
|
|
descriptor
|
|
|
|
.derived_descriptor(&Secp256k1::verification_only(), index)
|
|
|
|
.expect("must derive")
|
|
|
|
.script_pubkey()
|
|
|
|
}
|
|
|
|
|
2023-10-12 00:03:18 +08:00
|
|
|
#[test]
|
|
|
|
fn append_keychain_derivation_indices() {
|
|
|
|
#[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)]
|
|
|
|
enum Keychain {
|
|
|
|
One,
|
|
|
|
Two,
|
|
|
|
Three,
|
|
|
|
Four,
|
|
|
|
}
|
|
|
|
let mut lhs_di = BTreeMap::<Keychain, u32>::default();
|
|
|
|
let mut rhs_di = BTreeMap::<Keychain, u32>::default();
|
|
|
|
lhs_di.insert(Keychain::One, 7);
|
|
|
|
lhs_di.insert(Keychain::Two, 0);
|
|
|
|
rhs_di.insert(Keychain::One, 3);
|
|
|
|
rhs_di.insert(Keychain::Two, 5);
|
|
|
|
lhs_di.insert(Keychain::Three, 3);
|
|
|
|
rhs_di.insert(Keychain::Four, 4);
|
|
|
|
|
|
|
|
let mut lhs = ChangeSet(lhs_di);
|
|
|
|
let rhs = ChangeSet(rhs_di);
|
|
|
|
lhs.append(rhs);
|
|
|
|
|
|
|
|
// Exiting index doesn't update if the new index in `other` is lower than `self`.
|
|
|
|
assert_eq!(lhs.0.get(&Keychain::One), Some(&7));
|
|
|
|
// Existing index updates if the new index in `other` is higher than `self`.
|
|
|
|
assert_eq!(lhs.0.get(&Keychain::Two), Some(&5));
|
|
|
|
// Existing index is unchanged if keychain doesn't exist in `other`.
|
|
|
|
assert_eq!(lhs.0.get(&Keychain::Three), Some(&3));
|
|
|
|
// New keychain gets added if the keychain is in `other` but not in `self`.
|
|
|
|
assert_eq!(lhs.0.get(&Keychain::Four), Some(&4));
|
|
|
|
}
|
|
|
|
|
2023-03-01 11:09:08 +01:00
|
|
|
#[test]
|
|
|
|
fn test_set_all_derivation_indices() {
|
2023-08-16 17:39:35 +02:00
|
|
|
use bdk_chain::indexed_tx_graph::Indexer;
|
|
|
|
|
2023-11-28 18:08:49 +01:00
|
|
|
let (mut txout_index, _, _) = init_txout_index(0);
|
2023-03-01 11:09:08 +01:00
|
|
|
let derive_to: BTreeMap<_, _> =
|
|
|
|
[(TestKeychain::External, 12), (TestKeychain::Internal, 24)].into();
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.reveal_to_target_multi(&derive_to).1.as_inner(),
|
|
|
|
&derive_to
|
|
|
|
);
|
|
|
|
assert_eq!(txout_index.last_revealed_indices(), &derive_to);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.reveal_to_target_multi(&derive_to).1,
|
2023-08-07 17:43:17 +02:00
|
|
|
keychain::ChangeSet::default(),
|
2023-03-01 11:09:08 +01:00
|
|
|
"no changes if we set to the same thing"
|
|
|
|
);
|
2023-08-16 17:39:35 +02:00
|
|
|
assert_eq!(txout_index.initial_changeset().as_inner(), &derive_to);
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_lookahead() {
|
2023-11-28 18:08:49 +01:00
|
|
|
let (mut txout_index, external_desc, internal_desc) = init_txout_index(10);
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
// given:
|
|
|
|
// - external lookahead set to 10
|
|
|
|
// when:
|
|
|
|
// - set external derivation index to value higher than last, but within the lookahead value
|
|
|
|
// expect:
|
|
|
|
// - scripts cached in spk_txout_index should increase correctly
|
|
|
|
// - stored scripts of external keychain should be of expected counts
|
|
|
|
for index in (0..20).skip_while(|i| i % 2 == 1) {
|
2023-08-07 17:43:17 +02:00
|
|
|
let (revealed_spks, revealed_changeset) =
|
2023-03-01 11:09:08 +01:00
|
|
|
txout_index.reveal_to_target(&TestKeychain::External, index);
|
|
|
|
assert_eq!(
|
|
|
|
revealed_spks.collect::<Vec<_>>(),
|
|
|
|
vec![(index, spk_at_index(&external_desc, index))],
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2023-08-07 17:43:17 +02:00
|
|
|
revealed_changeset.as_inner(),
|
2023-03-01 11:09:08 +01:00
|
|
|
&[(TestKeychain::External, index)].into()
|
|
|
|
);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.inner().all_spks().len(),
|
|
|
|
10 /* external lookahead */ +
|
2023-11-28 18:08:49 +01:00
|
|
|
10 /* internal lookahead */ +
|
2023-03-01 11:09:08 +01:00
|
|
|
index as usize + 1 /* `derived` count */
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index
|
2024-01-13 20:04:49 +08:00
|
|
|
.revealed_keychain_spks(&TestKeychain::External)
|
2023-03-01 11:09:08 +01:00
|
|
|
.count(),
|
|
|
|
index as usize + 1,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index
|
2024-01-13 20:04:49 +08:00
|
|
|
.revealed_keychain_spks(&TestKeychain::Internal)
|
2023-03-01 11:09:08 +01:00
|
|
|
.count(),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index
|
2024-01-13 20:04:49 +08:00
|
|
|
.unused_keychain_spks(&TestKeychain::External)
|
2023-03-01 11:09:08 +01:00
|
|
|
.count(),
|
|
|
|
index as usize + 1,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index
|
2024-01-13 20:04:49 +08:00
|
|
|
.unused_keychain_spks(&TestKeychain::Internal)
|
2023-03-01 11:09:08 +01:00
|
|
|
.count(),
|
|
|
|
0,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// given:
|
2023-11-28 18:08:49 +01:00
|
|
|
// - internal lookahead is 10
|
2023-03-01 11:09:08 +01:00
|
|
|
// - internal derivation index is `None`
|
|
|
|
// when:
|
|
|
|
// - derivation index is set ahead of current derivation index + lookahead
|
|
|
|
// expect:
|
|
|
|
// - scripts cached in spk_txout_index should increase correctly, a.k.a. no scripts are skipped
|
2023-08-07 17:43:17 +02:00
|
|
|
let (revealed_spks, revealed_changeset) =
|
2023-03-01 11:09:08 +01:00
|
|
|
txout_index.reveal_to_target(&TestKeychain::Internal, 24);
|
|
|
|
assert_eq!(
|
|
|
|
revealed_spks.collect::<Vec<_>>(),
|
|
|
|
(0..=24)
|
|
|
|
.map(|index| (index, spk_at_index(&internal_desc, index)))
|
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2023-08-07 17:43:17 +02:00
|
|
|
revealed_changeset.as_inner(),
|
2023-03-01 11:09:08 +01:00
|
|
|
&[(TestKeychain::Internal, 24)].into()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.inner().all_spks().len(),
|
|
|
|
10 /* external lookahead */ +
|
2023-11-28 18:08:49 +01:00
|
|
|
10 /* internal lookahead */ +
|
2023-03-01 11:09:08 +01:00
|
|
|
20 /* external stored index count */ +
|
|
|
|
25 /* internal stored index count */
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index
|
2024-01-13 20:04:49 +08:00
|
|
|
.revealed_keychain_spks(&TestKeychain::Internal)
|
2023-03-01 11:09:08 +01:00
|
|
|
.count(),
|
|
|
|
25,
|
|
|
|
);
|
|
|
|
|
|
|
|
// ensure derivation indices are expected for each keychain
|
|
|
|
let last_external_index = txout_index
|
|
|
|
.last_revealed_index(&TestKeychain::External)
|
|
|
|
.expect("already derived");
|
|
|
|
let last_internal_index = txout_index
|
|
|
|
.last_revealed_index(&TestKeychain::Internal)
|
|
|
|
.expect("already derived");
|
|
|
|
assert_eq!(last_external_index, 19);
|
|
|
|
assert_eq!(last_internal_index, 24);
|
|
|
|
|
|
|
|
// when:
|
|
|
|
// - scanning txouts with spks within stored indexes
|
|
|
|
// expect:
|
|
|
|
// - no changes to stored index counts
|
|
|
|
let external_iter = 0..=last_external_index;
|
|
|
|
let internal_iter = last_internal_index - last_external_index..=last_internal_index;
|
|
|
|
for (external_index, internal_index) in external_iter.zip(internal_iter) {
|
|
|
|
let tx = Transaction {
|
|
|
|
output: vec![
|
|
|
|
TxOut {
|
|
|
|
script_pubkey: external_desc
|
|
|
|
.at_derivation_index(external_index)
|
2023-06-21 17:59:34 +02:00
|
|
|
.unwrap()
|
2023-03-01 11:09:08 +01:00
|
|
|
.script_pubkey(),
|
2023-10-16 19:51:53 +11:00
|
|
|
value: Amount::from_sat(10_000),
|
2023-03-01 11:09:08 +01:00
|
|
|
},
|
|
|
|
TxOut {
|
|
|
|
script_pubkey: internal_desc
|
|
|
|
.at_derivation_index(internal_index)
|
2023-06-21 17:59:34 +02:00
|
|
|
.unwrap()
|
2023-03-01 11:09:08 +01:00
|
|
|
.script_pubkey(),
|
2023-10-16 19:51:53 +11:00
|
|
|
value: Amount::from_sat(10_000),
|
2023-03-01 11:09:08 +01:00
|
|
|
},
|
|
|
|
],
|
|
|
|
..common::new_tx(external_index)
|
|
|
|
};
|
2023-08-25 12:52:09 +03:00
|
|
|
assert_eq!(txout_index.index_tx(&tx), keychain::ChangeSet::default());
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(
|
|
|
|
txout_index.last_revealed_index(&TestKeychain::External),
|
|
|
|
Some(last_external_index)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.last_revealed_index(&TestKeychain::Internal),
|
|
|
|
Some(last_internal_index)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index
|
2024-01-13 20:04:49 +08:00
|
|
|
.revealed_keychain_spks(&TestKeychain::External)
|
2023-03-01 11:09:08 +01:00
|
|
|
.count(),
|
|
|
|
last_external_index as usize + 1,
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index
|
2024-01-13 20:04:49 +08:00
|
|
|
.revealed_keychain_spks(&TestKeychain::Internal)
|
2023-03-01 11:09:08 +01:00
|
|
|
.count(),
|
|
|
|
last_internal_index as usize + 1,
|
|
|
|
);
|
|
|
|
}
|
2023-03-15 15:38:25 +08:00
|
|
|
}
|
2023-03-01 11:09:08 +01:00
|
|
|
|
2023-03-15 15:38:25 +08:00
|
|
|
// when:
|
|
|
|
// - scanning txouts with spks above last stored index
|
|
|
|
// expect:
|
|
|
|
// - last revealed index should increase as expected
|
|
|
|
// - last used index should change as expected
|
|
|
|
#[test]
|
|
|
|
fn test_scan_with_lookahead() {
|
2023-11-28 18:08:49 +01:00
|
|
|
let (mut txout_index, external_desc, _) = init_txout_index(10);
|
2023-03-15 15:38:25 +08:00
|
|
|
|
2023-06-21 17:59:34 +02:00
|
|
|
let spks: BTreeMap<u32, ScriptBuf> = [0, 10, 20, 30]
|
2023-03-15 15:38:25 +08:00
|
|
|
.into_iter()
|
2023-06-21 17:59:34 +02:00
|
|
|
.map(|i| {
|
|
|
|
(
|
|
|
|
i,
|
|
|
|
external_desc
|
|
|
|
.at_derivation_index(i)
|
|
|
|
.unwrap()
|
|
|
|
.script_pubkey(),
|
|
|
|
)
|
|
|
|
})
|
2023-03-15 15:38:25 +08:00
|
|
|
.collect();
|
|
|
|
|
|
|
|
for (&spk_i, spk) in &spks {
|
|
|
|
let op = OutPoint::new(h!("fake tx"), spk_i);
|
|
|
|
let txout = TxOut {
|
|
|
|
script_pubkey: spk.clone(),
|
2023-10-16 19:51:53 +11:00
|
|
|
value: Amount::ZERO,
|
2023-03-15 15:38:25 +08:00
|
|
|
};
|
|
|
|
|
2023-08-25 12:52:09 +03:00
|
|
|
let changeset = txout_index.index_txout(op, &txout);
|
2023-03-15 15:38:25 +08:00
|
|
|
assert_eq!(
|
2023-08-07 17:43:17 +02:00
|
|
|
changeset.as_inner(),
|
2023-03-15 15:38:25 +08:00
|
|
|
&[(TestKeychain::External, spk_i)].into()
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.last_revealed_index(&TestKeychain::External),
|
|
|
|
Some(spk_i)
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.last_used_index(&TestKeychain::External),
|
|
|
|
Some(spk_i)
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
// now try with index 41 (lookahead surpassed), we expect that the txout to not be indexed
|
2023-06-21 17:59:34 +02:00
|
|
|
let spk_41 = external_desc
|
|
|
|
.at_derivation_index(41)
|
|
|
|
.unwrap()
|
|
|
|
.script_pubkey();
|
2023-03-15 15:38:25 +08:00
|
|
|
let op = OutPoint::new(h!("fake tx"), 41);
|
|
|
|
let txout = TxOut {
|
|
|
|
script_pubkey: spk_41,
|
2023-10-16 19:51:53 +11:00
|
|
|
value: Amount::ZERO,
|
2023-03-15 15:38:25 +08:00
|
|
|
};
|
2023-08-25 12:52:09 +03:00
|
|
|
let changeset = txout_index.index_txout(op, &txout);
|
2023-08-07 17:43:17 +02:00
|
|
|
assert!(changeset.is_empty());
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2023-06-21 17:59:34 +02:00
|
|
|
#[rustfmt::skip]
|
2023-03-01 11:09:08 +01:00
|
|
|
fn test_wildcard_derivations() {
|
2023-11-28 18:08:49 +01:00
|
|
|
let (mut txout_index, external_desc, _) = init_txout_index(0);
|
2023-06-21 17:59:34 +02:00
|
|
|
let external_spk_0 = external_desc.at_derivation_index(0).unwrap().script_pubkey();
|
|
|
|
let external_spk_16 = external_desc.at_derivation_index(16).unwrap().script_pubkey();
|
|
|
|
let external_spk_26 = external_desc.at_derivation_index(26).unwrap().script_pubkey();
|
|
|
|
let external_spk_27 = external_desc.at_derivation_index(27).unwrap().script_pubkey();
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
// - nothing is derived
|
|
|
|
// - unused list is also empty
|
|
|
|
//
|
|
|
|
// - next_derivation_index() == (0, true)
|
2023-08-07 17:43:17 +02:00
|
|
|
// - derive_new() == ((0, <spk>), keychain::ChangeSet)
|
|
|
|
// - next_unused() == ((0, <spk>), keychain::ChangeSet:is_empty())
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(txout_index.next_index(&TestKeychain::External), (0, true));
|
|
|
|
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External);
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0_u32, external_spk_0.as_script()));
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 0)].into());
|
|
|
|
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0_u32, external_spk_0.as_script()));
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(changeset.as_inner(), &[].into());
|
|
|
|
|
|
|
|
// - derived till 25
|
|
|
|
// - used all spks till 15.
|
|
|
|
// - used list : [0..=15, 17, 20, 23]
|
|
|
|
// - unused list: [16, 18, 19, 21, 22, 24, 25]
|
|
|
|
|
|
|
|
// - next_derivation_index() = (26, true)
|
2023-08-07 17:43:17 +02:00
|
|
|
// - derive_new() = ((26, <spk>), keychain::ChangeSet)
|
|
|
|
// - next_unused() == ((16, <spk>), keychain::ChangeSet::is_empty())
|
2023-03-01 11:09:08 +01:00
|
|
|
let _ = txout_index.reveal_to_target(&TestKeychain::External, 25);
|
|
|
|
|
|
|
|
(0..=15)
|
2023-11-16 07:17:16 +08:00
|
|
|
.chain([17, 20, 23])
|
2024-01-13 20:04:49 +08:00
|
|
|
.for_each(|index| assert!(txout_index.mark_used(TestKeychain::External, index)));
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
assert_eq!(txout_index.next_index(&TestKeychain::External), (26, true));
|
|
|
|
|
|
|
|
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External);
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (26, external_spk_26.as_script()));
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 26)].into());
|
|
|
|
|
|
|
|
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (16, external_spk_16.as_script()));
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(changeset.as_inner(), &[].into());
|
|
|
|
|
|
|
|
// - Use all the derived till 26.
|
2023-08-07 17:43:17 +02:00
|
|
|
// - next_unused() = ((27, <spk>), keychain::ChangeSet)
|
2023-04-27 19:39:21 +05:30
|
|
|
(0..=26).for_each(|index| {
|
2024-01-13 20:04:49 +08:00
|
|
|
txout_index.mark_used(TestKeychain::External, index);
|
2023-03-01 11:09:08 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (27, external_spk_27.as_script()));
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 27)].into());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_non_wildcard_derivations() {
|
2023-11-28 18:08:49 +01:00
|
|
|
let mut txout_index = KeychainTxOutIndex::<TestKeychain>::new(0);
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
let secp = bitcoin::secp256k1::Secp256k1::signing_only();
|
|
|
|
let (no_wildcard_descriptor, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap();
|
|
|
|
let external_spk = no_wildcard_descriptor
|
|
|
|
.at_derivation_index(0)
|
2023-06-21 17:59:34 +02:00
|
|
|
.unwrap()
|
2023-03-01 11:09:08 +01:00
|
|
|
.script_pubkey();
|
|
|
|
|
|
|
|
txout_index.add_keychain(TestKeychain::External, no_wildcard_descriptor);
|
|
|
|
|
|
|
|
// given:
|
|
|
|
// - `txout_index` with no stored scripts
|
|
|
|
// expect:
|
|
|
|
// - next derivation index should be new
|
|
|
|
// - when we derive a new script, script @ index 0
|
|
|
|
// - when we get the next unused script, script @ index 0
|
|
|
|
assert_eq!(txout_index.next_index(&TestKeychain::External), (0, true));
|
|
|
|
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External);
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0, external_spk.as_script()));
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(changeset.as_inner(), &[(TestKeychain::External, 0)].into());
|
|
|
|
|
|
|
|
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0, external_spk.as_script()));
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(changeset.as_inner(), &[].into());
|
|
|
|
|
|
|
|
// given:
|
|
|
|
// - the non-wildcard descriptor already has a stored and used script
|
|
|
|
// expect:
|
|
|
|
// - next derivation index should not be new
|
|
|
|
// - derive new and next unused should return the old script
|
2023-08-07 17:43:17 +02:00
|
|
|
// - store_up_to should not panic and return empty changeset
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(txout_index.next_index(&TestKeychain::External), (0, false));
|
2024-01-13 20:04:49 +08:00
|
|
|
txout_index.mark_used(TestKeychain::External, 0);
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External);
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0, external_spk.as_script()));
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(changeset.as_inner(), &[].into());
|
|
|
|
|
|
|
|
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External);
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0, external_spk.as_script()));
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(changeset.as_inner(), &[].into());
|
2023-08-07 17:43:17 +02:00
|
|
|
let (revealed_spks, revealed_changeset) =
|
2023-03-01 11:09:08 +01:00
|
|
|
txout_index.reveal_to_target(&TestKeychain::External, 200);
|
|
|
|
assert_eq!(revealed_spks.count(), 0);
|
2023-08-07 17:43:17 +02:00
|
|
|
assert!(revealed_changeset.is_empty());
|
2023-08-24 16:53:50 +02:00
|
|
|
|
|
|
|
// we check that spks_of_keychain returns a SpkIterator with just one element
|
|
|
|
assert_eq!(
|
|
|
|
txout_index
|
2024-01-13 20:04:49 +08:00
|
|
|
.revealed_keychain_spks(&TestKeychain::External)
|
2023-08-24 16:53:50 +02:00
|
|
|
.count(),
|
|
|
|
1,
|
|
|
|
);
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
2024-02-17 02:20:44 +08:00
|
|
|
|
|
|
|
/// Check that calling `lookahead_to_target` stores the expected spks.
|
|
|
|
#[test]
|
|
|
|
fn lookahead_to_target() {
|
|
|
|
#[derive(Default)]
|
|
|
|
struct TestCase {
|
2024-02-28 05:47:25 -03:00
|
|
|
/// Global lookahead value.
|
|
|
|
lookahead: u32,
|
|
|
|
/// Last revealed index for external keychain.
|
|
|
|
external_last_revealed: Option<u32>,
|
|
|
|
/// Last revealed index for internal keychain.
|
|
|
|
internal_last_revealed: Option<u32>,
|
|
|
|
/// Call `lookahead_to_target(External, u32)`.
|
|
|
|
external_target: Option<u32>,
|
|
|
|
/// Call `lookahead_to_target(Internal, u32)`.
|
|
|
|
internal_target: Option<u32>,
|
2024-02-17 02:20:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
let test_cases = &[
|
|
|
|
TestCase {
|
|
|
|
lookahead: 0,
|
|
|
|
external_target: Some(100),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
TestCase {
|
|
|
|
lookahead: 10,
|
|
|
|
internal_target: Some(99),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
TestCase {
|
|
|
|
lookahead: 100,
|
|
|
|
internal_target: Some(9),
|
|
|
|
external_target: Some(10),
|
|
|
|
..Default::default()
|
|
|
|
},
|
|
|
|
TestCase {
|
|
|
|
lookahead: 12,
|
|
|
|
external_last_revealed: Some(2),
|
|
|
|
internal_last_revealed: Some(2),
|
|
|
|
internal_target: Some(15),
|
|
|
|
external_target: Some(13),
|
|
|
|
},
|
|
|
|
TestCase {
|
|
|
|
lookahead: 13,
|
|
|
|
external_last_revealed: Some(100),
|
|
|
|
internal_last_revealed: Some(21),
|
|
|
|
internal_target: Some(120),
|
|
|
|
external_target: Some(130),
|
|
|
|
},
|
|
|
|
];
|
|
|
|
|
|
|
|
for t in test_cases {
|
|
|
|
let (mut index, _, _) = init_txout_index(t.lookahead);
|
|
|
|
|
|
|
|
if let Some(last_revealed) = t.external_last_revealed {
|
|
|
|
let _ = index.reveal_to_target(&TestKeychain::External, last_revealed);
|
|
|
|
}
|
|
|
|
if let Some(last_revealed) = t.internal_last_revealed {
|
|
|
|
let _ = index.reveal_to_target(&TestKeychain::Internal, last_revealed);
|
|
|
|
}
|
|
|
|
|
|
|
|
let keychain_test_cases = [
|
|
|
|
(
|
|
|
|
TestKeychain::External,
|
|
|
|
t.external_last_revealed,
|
|
|
|
t.external_target,
|
|
|
|
),
|
|
|
|
(
|
|
|
|
TestKeychain::Internal,
|
|
|
|
t.internal_last_revealed,
|
|
|
|
t.internal_target,
|
|
|
|
),
|
|
|
|
];
|
|
|
|
for (keychain, last_revealed, target) in keychain_test_cases {
|
|
|
|
if let Some(target) = target {
|
|
|
|
let original_last_stored_index = match last_revealed {
|
|
|
|
Some(last_revealed) => Some(last_revealed + t.lookahead),
|
|
|
|
None => t.lookahead.checked_sub(1),
|
|
|
|
};
|
|
|
|
let exp_last_stored_index = match original_last_stored_index {
|
|
|
|
Some(original_last_stored_index) => {
|
|
|
|
Ord::max(target, original_last_stored_index)
|
|
|
|
}
|
|
|
|
None => target,
|
|
|
|
};
|
|
|
|
index.lookahead_to_target(&keychain, target);
|
|
|
|
let keys = index
|
|
|
|
.inner()
|
|
|
|
.all_spks()
|
|
|
|
.range((keychain.clone(), 0)..=(keychain.clone(), u32::MAX))
|
|
|
|
.map(|(k, _)| k.clone())
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
let exp_keys = core::iter::repeat(keychain)
|
|
|
|
.zip(0_u32..=exp_last_stored_index)
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(keys, exp_keys);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|