]> Untitled Git - bdk/commitdiff
[bdk_chain_redesign] Make `insert_relevant_txs` topologically-agnostic
author志宇 <hello@evanlinjin.me>
Sat, 22 Apr 2023 14:56:51 +0000 (22:56 +0800)
committer志宇 <hello@evanlinjin.me>
Sat, 22 Apr 2023 15:09:39 +0000 (23:09 +0800)
The `insert_relevant_txs` test has also been changed to used
`KeychainTxOutIndex` so that index additions can be checked
(`SpkTxOutIndex` has no additions).

Additionally, generic bounds of some `IndexedTxGraph` list methods have
been fixed.

crates/chain/src/indexed_tx_graph.rs
crates/chain/tests/test_indexed_tx_graph.rs

index 60a6c646b0fe32f93706a626f17ff7ffaf311d5a..20d9958ea0f7205ddefdd817288c198cca8e5d26 100644 (file)
@@ -117,7 +117,7 @@ where
     /// 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.
@@ -127,28 +127,31 @@ where
         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
     }
 }
 
@@ -170,7 +173,7 @@ impl<A: Anchor, I: OwnedIndexer> IndexedTxGraph<A, I> {
             })
     }
 
-    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,
@@ -196,14 +199,11 @@ impl<A: Anchor, I: OwnedIndexer> IndexedTxGraph<A, I> {
             })
     }
 
-    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"))
     }
index e85d424e43cfc3f049ae88ae9e8ba89e9b874b07..26a30cb87fcbd9f868b6d99ff5410dec740a918d 100644 (file)
@@ -2,10 +2,12 @@ mod common;
 
 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.
@@ -16,13 +18,15 @@ use bitcoin::{hashes::hex::FromHex, OutPoint, Script, Transaction, TxIn, TxOut};
 /// 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![
@@ -63,7 +67,7 @@ fn insert_relevant_txs() {
                 tx: txs.into(),
                 ..Default::default()
             },
-            ..Default::default()
+            index_additions: DerivationAdditions([((), 9_u32)].into()),
         }
     )
 }