/// Insert relevant transactions from the given `txs` iterator.
///
/// Relevancy is determined by the [`Indexer::is_tx_relevant`] implementation of `I`. Irrelevant
- /// transactions in `txs` will be ignored.
+ /// transactions in `txs` will be ignored. Also, `txs` does not need to be in topological order.
///
/// `anchors` can be provided to anchor the transactions to blocks. `seen_at` is a unix
/// timestamp of when the transactions are last seen.
anchors: impl IntoIterator<Item = A> + Clone,
seen_at: Option<u64>,
) -> IndexedAdditions<A, I::Additions> {
- // As mentioned by @LLFourn: This algorithm requires the transactions to be topologically
- // sorted because most indexers cannot decide whether something is relevant unless you have
- // first inserted its ancestors in the index. We can fix this if we instead do this:
+ // The algorithm below allows for non-topologically ordered transactions by using two loops.
+ // This is achieved by:
// 1. insert all txs into the index. If they are irrelevant then that's fine it will just
// not store anything about them.
// 2. decide whether to insert them into the graph depending on whether `is_tx_relevant`
// returns true or not. (in a second loop).
- let txs = txs
- .into_iter()
- .inspect(|tx| {
- let _ = self.index.index_tx(tx);
- })
- .collect::<Vec<_>>();
- txs.into_iter()
- .filter_map(|tx| match self.index.is_tx_relevant(tx) {
- true => Some(self.insert_tx(tx, anchors.clone(), seen_at)),
- false => None,
- })
- .fold(Default::default(), |mut acc, other| {
- acc.append(other);
- acc
- })
+ let mut additions = IndexedAdditions::<A, I::Additions>::default();
+ let mut transactions = Vec::new();
+ for tx in txs.into_iter() {
+ additions.index_additions.append(self.index.index_tx(tx));
+ transactions.push(tx);
+ }
+ additions.append(
+ transactions
+ .into_iter()
+ .filter_map(|tx| match self.index.is_tx_relevant(tx) {
+ true => Some(self.insert_tx(tx, anchors.clone(), seen_at)),
+ false => None,
+ })
+ .fold(Default::default(), |mut acc, other| {
+ acc.append(other);
+ acc
+ }),
+ );
+ additions
}
}
})
}
- pub fn list_owned_txouts<'a, C: ChainOracle + 'a>(
+ pub fn list_owned_txouts<'a, C: ChainOracle<Error = Infallible> + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
})
}
- pub fn list_owned_unspents<'a, C>(
+ pub fn list_owned_unspents<'a, C: ChainOracle<Error = Infallible> + 'a>(
&'a self,
chain: &'a C,
chain_tip: BlockId,
- ) -> impl Iterator<Item = FullTxOut<ObservedAs<A>>> + 'a
- where
- C: ChainOracle + 'a,
- {
+ ) -> impl Iterator<Item = FullTxOut<ObservedAs<A>>> + 'a {
self.try_list_owned_unspents(chain, chain_tip)
.map(|r| r.expect("oracle is infallible"))
}
use bdk_chain::{
indexed_tx_graph::{IndexedAdditions, IndexedTxGraph},
+ keychain::{DerivationAdditions, KeychainTxOutIndex},
tx_graph::Additions,
- BlockId, SpkTxOutIndex,
+ BlockId,
};
-use bitcoin::{hashes::hex::FromHex, OutPoint, Script, Transaction, TxIn, TxOut};
+use bitcoin::{secp256k1::Secp256k1, OutPoint, Transaction, TxIn, TxOut};
+use miniscript::Descriptor;
/// Ensure [`IndexedTxGraph::insert_relevant_txs`] can successfully index transactions NOT presented
/// in topological order.
/// agnostic.
#[test]
fn insert_relevant_txs() {
- let mut graph = IndexedTxGraph::<BlockId, SpkTxOutIndex<u32>>::default();
+ const DESCRIPTOR: &str = "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)";
+ let (descriptor, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), DESCRIPTOR)
+ .expect("must be valid");
+ let spk_0 = descriptor.at_derivation_index(0).script_pubkey();
+ let spk_1 = descriptor.at_derivation_index(9).script_pubkey();
- // insert some spks
- let spk_0 = Script::from_hex("0014034f9515cace31713707dff8194b8f550eb6d336").unwrap();
- let spk_1 = Script::from_hex("0014beaa39ab2b4f47995c77107d8c3f481d3bd33941").unwrap();
- graph.index.insert_spk(0, spk_0.clone());
- graph.index.insert_spk(1, spk_1.clone());
+ let mut graph = IndexedTxGraph::<BlockId, KeychainTxOutIndex<()>>::default();
+ graph.index.add_keychain((), descriptor);
+ graph.index.set_lookahead(&(), 10);
let tx_a = Transaction {
output: vec![
tx: txs.into(),
..Default::default()
},
- ..Default::default()
+ index_additions: DerivationAdditions([((), 9_u32)].into()),
}
)
}