refactor(keychain): Fix KeychainTxOutIndex range queries
The underlying SpkTxOutIndex should not use DescriptorIds to index because this loses the ordering relationship of the spks so queries on subranges of keychains work. Along with that we enforce that there is a strict 1-to-1 relationship between descriptors and keychains. Violating this leads to an error in insert_descriptor now. In general I try to make the translation layer between the SpkTxOutIndex and the KeychainTxOutIndex thinner. Ergonomics of this will be improved in next commit. The test from the previous commit passes.
This commit is contained in:
@@ -23,7 +23,6 @@ pub enum Error {
|
||||
HardenedDerivationXpub,
|
||||
/// The descriptor contains multipath keys
|
||||
MultiPath,
|
||||
|
||||
/// Error thrown while working with [`keys`](crate::keys)
|
||||
Key(crate::keys::KeyError),
|
||||
/// Error while extracting and manipulating policies
|
||||
|
||||
@@ -772,7 +772,7 @@ impl Wallet {
|
||||
keychain: KeychainKind,
|
||||
index: u32,
|
||||
) -> anyhow::Result<impl Iterator<Item = AddressInfo> + '_> {
|
||||
let (spk_iter, index_changeset) = self
|
||||
let (spks, index_changeset) = self
|
||||
.indexed_graph
|
||||
.index
|
||||
.reveal_to_target(&keychain, index)
|
||||
@@ -781,7 +781,7 @@ impl Wallet {
|
||||
self.persist
|
||||
.stage_and_commit(indexed_tx_graph::ChangeSet::from(index_changeset).into())?;
|
||||
|
||||
Ok(spk_iter.map(move |(index, spk)| AddressInfo {
|
||||
Ok(spks.into_iter().map(move |(index, spk)| AddressInfo {
|
||||
index,
|
||||
address: Address::from_script(&spk, self.network).expect("must have address form"),
|
||||
keychain,
|
||||
@@ -861,7 +861,7 @@ impl Wallet {
|
||||
///
|
||||
/// Will only return `Some(_)` if the wallet has given out the spk.
|
||||
pub fn derivation_of_spk(&self, spk: &Script) -> Option<(KeychainKind, u32)> {
|
||||
self.indexed_graph.index.index_of_spk(spk)
|
||||
self.indexed_graph.index.index_of_spk(spk).cloned()
|
||||
}
|
||||
|
||||
/// Return the list of unspent outputs of this wallet
|
||||
@@ -871,7 +871,7 @@ impl Wallet {
|
||||
.filter_chain_unspents(
|
||||
&self.chain,
|
||||
self.chain.tip().block_id(),
|
||||
self.indexed_graph.index.outpoints(),
|
||||
self.indexed_graph.index.outpoints().iter().cloned(),
|
||||
)
|
||||
.map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
|
||||
}
|
||||
@@ -885,7 +885,7 @@ impl Wallet {
|
||||
.filter_chain_txouts(
|
||||
&self.chain,
|
||||
self.chain.tip().block_id(),
|
||||
self.indexed_graph.index.outpoints(),
|
||||
self.indexed_graph.index.outpoints().iter().cloned(),
|
||||
)
|
||||
.map(|((k, i), full_txo)| new_local_utxo(k, i, full_txo))
|
||||
}
|
||||
@@ -932,7 +932,7 @@ impl Wallet {
|
||||
/// Returns the utxo owned by this wallet corresponding to `outpoint` if it exists in the
|
||||
/// wallet's database.
|
||||
pub fn get_utxo(&self, op: OutPoint) -> Option<LocalOutput> {
|
||||
let (keychain, index, _) = self.indexed_graph.index.txout(op)?;
|
||||
let (&(keychain, index), _) = self.indexed_graph.index.txout(op)?;
|
||||
self.indexed_graph
|
||||
.graph()
|
||||
.filter_chain_unspents(
|
||||
@@ -1207,7 +1207,7 @@ impl Wallet {
|
||||
self.indexed_graph.graph().balance(
|
||||
&self.chain,
|
||||
self.chain.tip().block_id(),
|
||||
self.indexed_graph.index.outpoints(),
|
||||
self.indexed_graph.index.outpoints().iter().cloned(),
|
||||
|&(k, _), _| k == KeychainKind::Internal,
|
||||
)
|
||||
}
|
||||
@@ -1699,7 +1699,7 @@ impl Wallet {
|
||||
.into();
|
||||
|
||||
let weighted_utxo = match txout_index.index_of_spk(&txout.script_pubkey) {
|
||||
Some((keychain, derivation_index)) => {
|
||||
Some(&(keychain, derivation_index)) => {
|
||||
let satisfaction_weight = self
|
||||
.get_descriptor_for_keychain(keychain)
|
||||
.max_weight_to_satisfy()
|
||||
@@ -1744,7 +1744,7 @@ impl Wallet {
|
||||
for (index, txout) in tx.output.iter().enumerate() {
|
||||
let change_keychain = KeychainKind::Internal;
|
||||
match txout_index.index_of_spk(&txout.script_pubkey) {
|
||||
Some((keychain, _)) if keychain == change_keychain => {
|
||||
Some((keychain, _)) if *keychain == change_keychain => {
|
||||
change_index = Some(index)
|
||||
}
|
||||
_ => {}
|
||||
@@ -2015,13 +2015,13 @@ impl Wallet {
|
||||
if let Some((keychain, index)) = txout_index.index_of_spk(&txout.script_pubkey) {
|
||||
// NOTE: unmark_used will **not** make something unused if it has actually been used
|
||||
// by a tx in the tracker. It only removes the superficial marking.
|
||||
txout_index.unmark_used(keychain, index);
|
||||
txout_index.unmark_used(*keychain, *index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_descriptor_for_txout(&self, txout: &TxOut) -> Option<DerivedDescriptor> {
|
||||
let (keychain, child) = self
|
||||
let &(keychain, child) = self
|
||||
.indexed_graph
|
||||
.index
|
||||
.index_of_spk(&txout.script_pubkey)?;
|
||||
@@ -2237,7 +2237,7 @@ impl Wallet {
|
||||
) -> Result<psbt::Input, CreateTxError> {
|
||||
// Try to find the prev_script in our db to figure out if this is internal or external,
|
||||
// and the derivation index
|
||||
let (keychain, child) = self
|
||||
let &(keychain, child) = self
|
||||
.indexed_graph
|
||||
.index
|
||||
.index_of_spk(&utxo.txout.script_pubkey)
|
||||
@@ -2285,7 +2285,7 @@ impl Wallet {
|
||||
|
||||
// Try to figure out the keychain and derivation for every input and output
|
||||
for (is_input, index, out) in utxos.into_iter() {
|
||||
if let Some((keychain, child)) =
|
||||
if let Some(&(keychain, child)) =
|
||||
self.indexed_graph.index.index_of_spk(&out.script_pubkey)
|
||||
{
|
||||
let desc = self.get_descriptor_for_keychain(keychain);
|
||||
@@ -2331,7 +2331,7 @@ impl Wallet {
|
||||
None => ChangeSet::default(),
|
||||
};
|
||||
|
||||
let (_, index_changeset) = self
|
||||
let index_changeset = self
|
||||
.indexed_graph
|
||||
.index
|
||||
.reveal_to_target_multi(&update.last_active_indices);
|
||||
@@ -2536,17 +2536,27 @@ fn create_signers<E: IntoWalletDescriptor>(
|
||||
) -> Result<(Arc<SignersContainer>, Arc<SignersContainer>), DescriptorError> {
|
||||
let descriptor = into_wallet_descriptor_checked(descriptor, secp, network)?;
|
||||
let change_descriptor = into_wallet_descriptor_checked(change_descriptor, secp, network)?;
|
||||
if descriptor.0 == change_descriptor.0 {
|
||||
return Err(DescriptorError::ExternalAndInternalAreTheSame);
|
||||
}
|
||||
|
||||
let (descriptor, keymap) = descriptor;
|
||||
let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
|
||||
let _ = index.insert_descriptor(KeychainKind::External, descriptor);
|
||||
let _ = index
|
||||
.insert_descriptor(KeychainKind::External, descriptor)
|
||||
.expect("this is the first descriptor we're inserting");
|
||||
|
||||
let (descriptor, keymap) = change_descriptor;
|
||||
let change_signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
|
||||
let _ = index.insert_descriptor(KeychainKind::Internal, descriptor);
|
||||
let _ = index
|
||||
.insert_descriptor(KeychainKind::Internal, descriptor)
|
||||
.map_err(|e| {
|
||||
use bdk_chain::keychain::InsertDescriptorError;
|
||||
match e {
|
||||
InsertDescriptorError::DescriptorAlreadyAssigned { .. } => {
|
||||
crate::descriptor::error::Error::ExternalAndInternalAreTheSame
|
||||
}
|
||||
InsertDescriptorError::KeychainAlreadyAssigned { .. } => {
|
||||
unreachable!("this is the first time we're assigning internal")
|
||||
}
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok((signers, change_signers))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user