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},
|
2024-01-15 18:52:03 +01:00
|
|
|
Append, DescriptorExt, DescriptorId,
|
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};
|
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
use crate::common::DESCRIPTORS;
|
|
|
|
|
2023-03-01 11:09:08 +01:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
|
|
|
|
enum TestKeychain {
|
|
|
|
External,
|
|
|
|
Internal,
|
|
|
|
}
|
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
fn parse_descriptor(descriptor: &str) -> Descriptor<DescriptorPublicKey> {
|
|
|
|
let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
|
|
|
|
Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, descriptor)
|
|
|
|
.unwrap()
|
|
|
|
.0
|
|
|
|
}
|
|
|
|
|
2023-11-28 18:08:49 +01:00
|
|
|
fn init_txout_index(
|
2024-01-15 18:52:03 +01:00
|
|
|
external_descriptor: Descriptor<DescriptorPublicKey>,
|
|
|
|
internal_descriptor: Descriptor<DescriptorPublicKey>,
|
2023-11-28 18:08:49 +01:00
|
|
|
lookahead: u32,
|
2024-01-15 18:52:03 +01:00
|
|
|
) -> bdk_chain::keychain::KeychainTxOutIndex<TestKeychain> {
|
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
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
let _ = txout_index.insert_descriptor(TestKeychain::External, external_descriptor);
|
|
|
|
let _ = txout_index.insert_descriptor(TestKeychain::Internal, internal_descriptor);
|
2023-03-01 11:09:08 +01:00
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
txout_index
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
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()
|
|
|
|
}
|
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
// We create two empty changesets lhs and rhs, we then insert various descriptors with various
|
|
|
|
// last_revealed, append rhs to lhs, and check that the result is consistent with these rules:
|
|
|
|
// - Existing index doesn't update if the new index in `other` is lower than `self`.
|
|
|
|
// - Existing index updates if the new index in `other` is higher than `self`.
|
|
|
|
// - Existing index is unchanged if keychain doesn't exist in `other`.
|
|
|
|
// - New keychain gets added if the keychain is in `other` but not in `self`.
|
2023-10-12 00:03:18 +08:00
|
|
|
#[test]
|
2024-01-15 18:52:03 +01:00
|
|
|
fn append_changesets_check_last_revealed() {
|
|
|
|
let secp = bitcoin::secp256k1::Secp256k1::signing_only();
|
|
|
|
let descriptor_ids: Vec<_> = DESCRIPTORS
|
|
|
|
.iter()
|
|
|
|
.take(4)
|
|
|
|
.map(|d| {
|
|
|
|
Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, d)
|
|
|
|
.unwrap()
|
|
|
|
.0
|
|
|
|
.descriptor_id()
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let mut lhs_di = BTreeMap::<DescriptorId, u32>::default();
|
|
|
|
let mut rhs_di = BTreeMap::<DescriptorId, u32>::default();
|
|
|
|
lhs_di.insert(descriptor_ids[0], 7);
|
|
|
|
lhs_di.insert(descriptor_ids[1], 0);
|
|
|
|
lhs_di.insert(descriptor_ids[2], 3);
|
|
|
|
|
|
|
|
rhs_di.insert(descriptor_ids[0], 3); // value less than lhs desc 0
|
|
|
|
rhs_di.insert(descriptor_ids[1], 5); // value more than lhs desc 1
|
|
|
|
lhs_di.insert(descriptor_ids[3], 4); // key doesn't exist in lhs
|
|
|
|
|
|
|
|
let mut lhs = ChangeSet {
|
|
|
|
keychains_added: BTreeMap::<(), _>::new(),
|
|
|
|
last_revealed: lhs_di,
|
|
|
|
};
|
|
|
|
let rhs = ChangeSet {
|
|
|
|
keychains_added: BTreeMap::<(), _>::new(),
|
|
|
|
last_revealed: rhs_di,
|
|
|
|
};
|
2023-10-12 00:03:18 +08:00
|
|
|
lhs.append(rhs);
|
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
// Existing index doesn't update if the new index in `other` is lower than `self`.
|
|
|
|
assert_eq!(lhs.last_revealed.get(&descriptor_ids[0]), Some(&7));
|
2023-10-12 00:03:18 +08:00
|
|
|
// Existing index updates if the new index in `other` is higher than `self`.
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(lhs.last_revealed.get(&descriptor_ids[1]), Some(&5));
|
2023-10-12 00:03:18 +08:00
|
|
|
// Existing index is unchanged if keychain doesn't exist in `other`.
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(lhs.last_revealed.get(&descriptor_ids[2]), Some(&3));
|
2023-10-12 00:03:18 +08:00
|
|
|
// New keychain gets added if the keychain is in `other` but not in `self`.
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(lhs.last_revealed.get(&descriptor_ids[3]), Some(&4));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_apply_changeset_with_different_descriptors_to_same_keychain() {
|
|
|
|
let external_descriptor = parse_descriptor(DESCRIPTORS[0]);
|
|
|
|
let internal_descriptor = parse_descriptor(DESCRIPTORS[1]);
|
|
|
|
let mut txout_index =
|
|
|
|
init_txout_index(external_descriptor.clone(), internal_descriptor.clone(), 0);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.keychains().collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
(&TestKeychain::External, &external_descriptor),
|
|
|
|
(&TestKeychain::Internal, &internal_descriptor)
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
let changeset = ChangeSet {
|
|
|
|
keychains_added: [(TestKeychain::External, internal_descriptor.clone())].into(),
|
|
|
|
last_revealed: [].into(),
|
|
|
|
};
|
|
|
|
txout_index.apply_changeset(changeset);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.keychains().collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
(&TestKeychain::External, &internal_descriptor),
|
|
|
|
(&TestKeychain::Internal, &internal_descriptor)
|
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
let changeset = ChangeSet {
|
|
|
|
keychains_added: [(TestKeychain::Internal, external_descriptor.clone())].into(),
|
|
|
|
last_revealed: [].into(),
|
|
|
|
};
|
|
|
|
txout_index.apply_changeset(changeset);
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.keychains().collect::<Vec<_>>(),
|
|
|
|
vec![
|
|
|
|
(&TestKeychain::External, &internal_descriptor),
|
|
|
|
(&TestKeychain::Internal, &external_descriptor)
|
|
|
|
]
|
|
|
|
);
|
2023-10-12 00:03:18 +08:00
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
let external_descriptor = parse_descriptor(DESCRIPTORS[0]);
|
|
|
|
let internal_descriptor = parse_descriptor(DESCRIPTORS[1]);
|
|
|
|
let mut txout_index =
|
|
|
|
init_txout_index(external_descriptor.clone(), internal_descriptor.clone(), 0);
|
2023-03-01 11:09:08 +01:00
|
|
|
let derive_to: BTreeMap<_, _> =
|
|
|
|
[(TestKeychain::External, 12), (TestKeychain::Internal, 24)].into();
|
2024-01-15 18:52:03 +01:00
|
|
|
let last_revealed: BTreeMap<_, _> = [
|
|
|
|
(external_descriptor.descriptor_id(), 12),
|
|
|
|
(internal_descriptor.descriptor_id(), 24),
|
|
|
|
]
|
|
|
|
.into();
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(
|
2024-01-15 18:52:03 +01:00
|
|
|
txout_index.reveal_to_target_multi(&derive_to).1,
|
|
|
|
ChangeSet {
|
|
|
|
keychains_added: BTreeMap::new(),
|
|
|
|
last_revealed: last_revealed.clone()
|
|
|
|
}
|
2023-03-01 11:09:08 +01:00
|
|
|
);
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(txout_index.last_revealed_indices(), derive_to);
|
2023-03-01 11:09:08 +01:00
|
|
|
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"
|
|
|
|
);
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(txout_index.initial_changeset().last_revealed, last_revealed);
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_lookahead() {
|
2024-01-15 18:52:03 +01:00
|
|
|
let external_descriptor = parse_descriptor(DESCRIPTORS[0]);
|
|
|
|
let internal_descriptor = parse_descriptor(DESCRIPTORS[1]);
|
|
|
|
let mut txout_index =
|
|
|
|
init_txout_index(external_descriptor.clone(), internal_descriptor.clone(), 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) {
|
2024-01-15 18:52:03 +01:00
|
|
|
let (revealed_spks, revealed_changeset) = txout_index
|
|
|
|
.reveal_to_target(&TestKeychain::External, index)
|
|
|
|
.unwrap();
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(
|
|
|
|
revealed_spks.collect::<Vec<_>>(),
|
2024-01-15 18:52:03 +01:00
|
|
|
vec![(index, spk_at_index(&external_descriptor, index))],
|
2023-03-01 11:09:08 +01:00
|
|
|
);
|
|
|
|
assert_eq!(
|
2024-01-15 18:52:03 +01:00
|
|
|
&revealed_changeset.last_revealed,
|
|
|
|
&[(external_descriptor.descriptor_id(), index)].into()
|
2023-03-01 11:09:08 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
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
|
2024-01-15 18:52:03 +01:00
|
|
|
let (revealed_spks, revealed_changeset) = txout_index
|
|
|
|
.reveal_to_target(&TestKeychain::Internal, 24)
|
|
|
|
.unwrap();
|
2023-03-01 11:09:08 +01:00
|
|
|
assert_eq!(
|
|
|
|
revealed_spks.collect::<Vec<_>>(),
|
|
|
|
(0..=24)
|
2024-01-15 18:52:03 +01:00
|
|
|
.map(|index| (index, spk_at_index(&internal_descriptor, index)))
|
2023-03-01 11:09:08 +01:00
|
|
|
.collect::<Vec<_>>(),
|
|
|
|
);
|
|
|
|
assert_eq!(
|
2024-01-15 18:52:03 +01:00
|
|
|
&revealed_changeset.last_revealed,
|
|
|
|
&[(internal_descriptor.descriptor_id(), 24)].into()
|
2023-03-01 11:09:08 +01:00
|
|
|
);
|
|
|
|
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 {
|
2024-01-15 18:52:03 +01:00
|
|
|
script_pubkey: external_descriptor
|
2023-03-01 11:09:08 +01:00
|
|
|
.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 {
|
2024-01-15 18:52:03 +01:00
|
|
|
script_pubkey: internal_descriptor
|
2023-03-01 11:09:08 +01:00
|
|
|
.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() {
|
2024-01-15 18:52:03 +01:00
|
|
|
let external_descriptor = parse_descriptor(DESCRIPTORS[0]);
|
|
|
|
let internal_descriptor = parse_descriptor(DESCRIPTORS[1]);
|
|
|
|
let mut txout_index =
|
|
|
|
init_txout_index(external_descriptor.clone(), internal_descriptor.clone(), 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,
|
2024-01-15 18:52:03 +01:00
|
|
|
external_descriptor
|
2023-06-21 17:59:34 +02:00
|
|
|
.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!(
|
2024-01-15 18:52:03 +01:00
|
|
|
&changeset.last_revealed,
|
|
|
|
&[(external_descriptor.descriptor_id(), spk_i)].into()
|
2023-03-15 15:38:25 +08:00
|
|
|
);
|
|
|
|
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
|
2024-01-15 18:52:03 +01:00
|
|
|
let spk_41 = external_descriptor
|
2023-06-21 17:59:34 +02:00
|
|
|
.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() {
|
2024-01-15 18:52:03 +01:00
|
|
|
let external_descriptor = parse_descriptor(DESCRIPTORS[0]);
|
|
|
|
let internal_descriptor = parse_descriptor(DESCRIPTORS[1]);
|
|
|
|
let mut txout_index = init_txout_index(external_descriptor.clone(), internal_descriptor.clone(), 0);
|
|
|
|
let external_spk_0 = external_descriptor.at_derivation_index(0).unwrap().script_pubkey();
|
|
|
|
let external_spk_16 = external_descriptor.at_derivation_index(16).unwrap().script_pubkey();
|
|
|
|
let external_spk_26 = external_descriptor.at_derivation_index(26).unwrap().script_pubkey();
|
|
|
|
let external_spk_27 = external_descriptor.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())
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(txout_index.next_index(&TestKeychain::External).unwrap(), (0, true));
|
|
|
|
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External).unwrap();
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0_u32, external_spk_0.as_script()));
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(&changeset.last_revealed, &[(external_descriptor.descriptor_id(), 0)].into());
|
|
|
|
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External).unwrap();
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0_u32, external_spk_0.as_script()));
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(&changeset.last_revealed, &[].into());
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
// - 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
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(txout_index.next_index(&TestKeychain::External).unwrap(), (26, true));
|
2023-03-01 11:09:08 +01:00
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
let (spk, changeset) = txout_index.reveal_next_spk(&TestKeychain::External).unwrap();
|
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
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(&changeset.last_revealed, &[(external_descriptor.descriptor_id(), 26)].into());
|
2023-03-01 11:09:08 +01:00
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External).unwrap();
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (16, external_spk_16.as_script()));
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(&changeset.last_revealed, &[].into());
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
// - 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
|
|
|
});
|
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
let (spk, changeset) = txout_index.next_unused_spk(&TestKeychain::External).unwrap();
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (27, external_spk_27.as_script()));
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(&changeset.last_revealed, &[(external_descriptor.descriptor_id(), 27)].into());
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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();
|
2024-01-15 18:52:03 +01:00
|
|
|
let (no_wildcard_descriptor, _) =
|
|
|
|
Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, DESCRIPTORS[6]).unwrap();
|
2023-03-01 11:09:08 +01:00
|
|
|
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();
|
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
let _ = txout_index.insert_descriptor(TestKeychain::External, no_wildcard_descriptor.clone());
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
// 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
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(
|
|
|
|
txout_index.next_index(&TestKeychain::External).unwrap(),
|
|
|
|
(0, true)
|
|
|
|
);
|
|
|
|
let (spk, changeset) = txout_index
|
|
|
|
.reveal_next_spk(&TestKeychain::External)
|
|
|
|
.unwrap();
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0, external_spk.as_script()));
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(
|
|
|
|
&changeset.last_revealed,
|
|
|
|
&[(no_wildcard_descriptor.descriptor_id(), 0)].into()
|
|
|
|
);
|
2023-03-01 11:09:08 +01:00
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
let (spk, changeset) = txout_index
|
|
|
|
.next_unused_spk(&TestKeychain::External)
|
|
|
|
.unwrap();
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0, external_spk.as_script()));
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(&changeset.last_revealed, &[].into());
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
// 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
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(
|
|
|
|
txout_index.next_index(&TestKeychain::External).unwrap(),
|
|
|
|
(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
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
let (spk, changeset) = txout_index
|
|
|
|
.reveal_next_spk(&TestKeychain::External)
|
|
|
|
.unwrap();
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0, external_spk.as_script()));
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(&changeset.last_revealed, &[].into());
|
2023-03-01 11:09:08 +01:00
|
|
|
|
2024-01-15 18:52:03 +01:00
|
|
|
let (spk, changeset) = txout_index
|
|
|
|
.next_unused_spk(&TestKeychain::External)
|
|
|
|
.unwrap();
|
2023-06-21 17:59:34 +02:00
|
|
|
assert_eq!(spk, (0, external_spk.as_script()));
|
2024-01-15 18:52:03 +01:00
|
|
|
assert_eq!(&changeset.last_revealed, &[].into());
|
|
|
|
let (revealed_spks, revealed_changeset) = txout_index
|
|
|
|
.reveal_to_target(&TestKeychain::External, 200)
|
|
|
|
.unwrap();
|
2023-03-01 11:09:08 +01:00
|
|
|
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 {
|
2024-01-15 18:52:03 +01:00
|
|
|
let external_descriptor = parse_descriptor(DESCRIPTORS[0]);
|
|
|
|
let internal_descriptor = parse_descriptor(DESCRIPTORS[1]);
|
|
|
|
let mut index = init_txout_index(
|
|
|
|
external_descriptor.clone(),
|
|
|
|
internal_descriptor.clone(),
|
|
|
|
t.lookahead,
|
|
|
|
);
|
2024-02-17 02:20:44 +08:00
|
|
|
|
|
|
|
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 = [
|
|
|
|
(
|
2024-01-15 18:52:03 +01:00
|
|
|
external_descriptor.descriptor_id(),
|
2024-02-17 02:20:44 +08:00
|
|
|
TestKeychain::External,
|
|
|
|
t.external_last_revealed,
|
|
|
|
t.external_target,
|
|
|
|
),
|
|
|
|
(
|
2024-01-15 18:52:03 +01:00
|
|
|
internal_descriptor.descriptor_id(),
|
2024-02-17 02:20:44 +08:00
|
|
|
TestKeychain::Internal,
|
|
|
|
t.internal_last_revealed,
|
|
|
|
t.internal_target,
|
|
|
|
),
|
|
|
|
];
|
2024-01-15 18:52:03 +01:00
|
|
|
for (descriptor_id, keychain, last_revealed, target) in keychain_test_cases {
|
2024-02-17 02:20:44 +08:00
|
|
|
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()
|
2024-01-15 18:52:03 +01:00
|
|
|
.range((descriptor_id, 0)..=(descriptor_id, u32::MAX))
|
|
|
|
.map(|(k, _)| *k)
|
2024-02-17 02:20:44 +08:00
|
|
|
.collect::<Vec<_>>();
|
2024-01-15 18:52:03 +01:00
|
|
|
let exp_keys = core::iter::repeat(descriptor_id)
|
2024-02-17 02:20:44 +08:00
|
|
|
.zip(0_u32..=exp_last_stored_index)
|
|
|
|
.collect::<Vec<_>>();
|
|
|
|
assert_eq!(keys, exp_keys);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-01-15 18:52:03 +01:00
|
|
|
|
|
|
|
/// `::index_txout` should still index txouts with spks derived from descriptors without keychains.
|
|
|
|
/// This includes properly refilling the lookahead for said descriptors.
|
|
|
|
#[test]
|
|
|
|
fn index_txout_after_changing_descriptor_under_keychain() {
|
|
|
|
let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
|
|
|
|
let (desc_a, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, DESCRIPTORS[0])
|
|
|
|
.expect("descriptor 0 must be valid");
|
|
|
|
let (desc_b, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, DESCRIPTORS[1])
|
|
|
|
.expect("descriptor 1 must be valid");
|
|
|
|
let desc_id_a = desc_a.descriptor_id();
|
|
|
|
|
|
|
|
let mut txout_index = bdk_chain::keychain::KeychainTxOutIndex::<()>::new(10);
|
|
|
|
|
|
|
|
// Introduce `desc_a` under keychain `()` and replace the descriptor.
|
|
|
|
let _ = txout_index.insert_descriptor((), desc_a.clone());
|
|
|
|
let _ = txout_index.insert_descriptor((), desc_b.clone());
|
|
|
|
|
|
|
|
// Loop through spks in intervals of `lookahead` to create outputs with. We should always be
|
|
|
|
// able to index these outputs if `lookahead` is respected.
|
|
|
|
let spk_indices = [9, 19, 29, 39];
|
|
|
|
for i in spk_indices {
|
|
|
|
let spk_at_index = desc_a
|
|
|
|
.at_derivation_index(i)
|
|
|
|
.expect("must derive")
|
|
|
|
.script_pubkey();
|
|
|
|
let index_changeset = txout_index.index_txout(
|
|
|
|
// Use spk derivation index as vout as we just want an unique outpoint.
|
|
|
|
OutPoint::new(h!("mock_tx"), i as _),
|
|
|
|
&TxOut {
|
|
|
|
value: Amount::from_sat(10_000),
|
|
|
|
script_pubkey: spk_at_index,
|
|
|
|
},
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
index_changeset,
|
|
|
|
bdk_chain::keychain::ChangeSet {
|
|
|
|
keychains_added: BTreeMap::default(),
|
|
|
|
last_revealed: [(desc_id_a, i)].into(),
|
|
|
|
},
|
|
|
|
"must always increase last active if impl respects lookahead"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn insert_descriptor_no_change() {
|
|
|
|
let secp = Secp256k1::signing_only();
|
|
|
|
let (desc, _) =
|
|
|
|
Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, DESCRIPTORS[0]).unwrap();
|
|
|
|
let mut txout_index = KeychainTxOutIndex::<()>::default();
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.insert_descriptor((), desc.clone()),
|
|
|
|
keychain::ChangeSet {
|
|
|
|
keychains_added: [((), desc.clone())].into(),
|
|
|
|
last_revealed: Default::default()
|
|
|
|
},
|
|
|
|
);
|
|
|
|
assert_eq!(
|
|
|
|
txout_index.insert_descriptor((), desc.clone()),
|
|
|
|
keychain::ChangeSet::default(),
|
|
|
|
"inserting the same descriptor for keychain should return an empty changeset",
|
|
|
|
);
|
|
|
|
}
|