Skip to content

Commit b9aaf05

Browse files
committed
feat(chain): add input/output indices to sent_and_received_txouts
Return tuple of (index, TxOut) in sent_and_received_txouts methods to identify which input/output positions the TxOuts correspond to in the original transaction.
1 parent c1b4c5f commit b9aaf05

File tree

5 files changed

+76
-46
lines changed

5 files changed

+76
-46
lines changed

crates/chain/src/indexer.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
//! [`Indexer`] provides utilities for indexing transaction data.
22
33
use bitcoin::{OutPoint, Transaction, TxOut};
4+
use alloc::vec::Vec;
45

56
#[cfg(feature = "miniscript")]
67
pub mod keychain_txout;
78
pub mod spk_txout;
89

10+
/// Type alias for a list of indexed transaction outputs.
11+
///
12+
/// Each element is a tuple of `(index, TxOut)` where index is the index of the input or output in
13+
/// the original [`Transaction`].
14+
pub type IndexedTxOuts = Vec<(usize, TxOut)>;
15+
916
/// Utilities for indexing transaction data.
1017
///
1118
/// Types which implement this trait can be used to construct an [`IndexedTxGraph`].

crates/chain/src/indexer/keychain_txout.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use crate::{
88
spk_client::{FullScanRequestBuilder, SyncRequestBuilder},
99
spk_iter::BIP32_MAX_INDEX,
1010
spk_txout::SpkTxOutIndex,
11-
DescriptorExt, DescriptorId, Indexed, Indexer, KeychainIndexed, SpkIterator,
11+
DescriptorExt, DescriptorId, Indexed, IndexedTxOuts, Indexer, KeychainIndexed, SpkIterator,
1212
};
1313
use alloc::{borrow::ToOwned, vec::Vec};
1414
use bitcoin::{
@@ -428,7 +428,7 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
428428
&self,
429429
tx: &Transaction,
430430
range: impl RangeBounds<K>,
431-
) -> (Vec<TxOut>, Vec<TxOut>) {
431+
) -> (IndexedTxOuts, IndexedTxOuts) {
432432
self.inner
433433
.sent_and_received_txouts(tx, self.map_to_inner_bounds(range))
434434
}

crates/chain/src/indexer/spk_txout.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use core::ops::RangeBounds;
55

66
use crate::{
77
collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap},
8-
Indexer,
8+
IndexedTxOuts, Indexer,
99
};
1010
use bitcoin::{Amount, OutPoint, Script, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
1111

@@ -348,15 +348,15 @@ impl<I: Clone + Ord + core::fmt::Debug> SpkTxOutIndex<I> {
348348
///
349349
/// // Display addresses and amounts
350350
/// println!("Sent:");
351-
/// for txout in sent_txouts {
351+
/// for (i, txout) in sent_txouts {
352352
/// let address = Address::from_script(&txout.script_pubkey, Network::Bitcoin)?;
353-
/// println!(" from {} - {} sats", address, txout.value.to_sat());
353+
/// println!("input {}: from {} - {} sats", i, address, txout.value.to_sat());
354354
/// }
355355
///
356356
/// println!("Received:");
357-
/// for txout in received_txouts {
357+
/// for (i, txout) in received_txouts {
358358
/// let address = Address::from_script(&txout.script_pubkey, Network::Bitcoin)?;
359-
/// println!(" to {} - {} sats", address, txout.value.to_sat());
359+
/// println!("output {}: to {} + {} sats", i, address, txout.value.to_sat());
360360
/// }
361361
/// # Ok(())
362362
/// # }
@@ -365,22 +365,22 @@ impl<I: Clone + Ord + core::fmt::Debug> SpkTxOutIndex<I> {
365365
&self,
366366
tx: &Transaction,
367367
range: impl RangeBounds<I>,
368-
) -> (Vec<TxOut>, Vec<TxOut>) {
368+
) -> (IndexedTxOuts, IndexedTxOuts) {
369369
let mut sent = Vec::new();
370370
let mut received = Vec::new();
371371

372-
for txin in &tx.input {
372+
for (i, txin) in tx.input.iter().enumerate() {
373373
if let Some((index, txout)) = self.txout(txin.previous_output) {
374374
if range.contains(index) {
375-
sent.push(txout.clone());
375+
sent.push((i, txout.clone()));
376376
}
377377
}
378378
}
379379

380-
for txout in &tx.output {
380+
for (i, txout) in tx.output.iter().enumerate() {
381381
if let Some(index) = self.index_of_spk(txout.script_pubkey.clone()) {
382382
if range.contains(index) {
383-
received.push(txout.clone());
383+
received.push((i, txout.clone()));
384384
}
385385
}
386386
}

crates/chain/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pub mod indexed_tx_graph;
3636
pub use indexed_tx_graph::IndexedTxGraph;
3737
pub mod indexer;
3838
pub use indexer::spk_txout;
39-
pub use indexer::Indexer;
39+
pub use indexer::{IndexedTxOuts, Indexer};
4040
pub mod local_chain;
4141
mod tx_data_traits;
4242
pub use tx_data_traits::*;

crates/chain/tests/test_spk_txout_index.rs

Lines changed: 56 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -98,24 +98,29 @@ fn spk_txout_sent_and_received_txouts() {
9898
script_pubkey: spk1.clone(),
9999
}],
100100
};
101-
102101
let (sent_txouts, received_txouts) = index.sent_and_received_txouts(&tx1, ..);
103102
assert!(sent_txouts.is_empty());
104103
assert_eq!(
105104
received_txouts,
106-
vec![TxOut {
107-
value: Amount::from_sat(42_000),
108-
script_pubkey: spk1.clone(),
109-
}]
105+
vec![(
106+
0,
107+
TxOut {
108+
value: Amount::from_sat(42_000),
109+
script_pubkey: spk1.clone(),
110+
}
111+
)]
110112
);
111113
let (sent_txouts, received_txouts) = index.sent_and_received_txouts(&tx1, ..1);
112114
assert!(sent_txouts.is_empty());
113115
assert_eq!(
114116
received_txouts,
115-
vec![TxOut {
116-
value: Amount::from_sat(42_000),
117-
script_pubkey: spk1.clone(),
118-
}]
117+
vec![(
118+
0,
119+
TxOut {
120+
value: Amount::from_sat(42_000),
121+
script_pubkey: spk1.clone(),
122+
}
123+
)]
119124
);
120125
let (sent_txouts, received_txouts) = index.sent_and_received_txouts(&tx1, 1..);
121126
assert!(sent_txouts.is_empty() && received_txouts.is_empty());
@@ -147,49 +152,67 @@ fn spk_txout_sent_and_received_txouts() {
147152
let (sent_txouts, received_txouts) = index.sent_and_received_txouts(&tx2, ..);
148153
assert_eq!(
149154
sent_txouts,
150-
vec![TxOut {
151-
value: Amount::from_sat(42_000),
152-
script_pubkey: spk1.clone(),
153-
}]
155+
vec![(
156+
0,
157+
TxOut {
158+
value: Amount::from_sat(42_000),
159+
script_pubkey: spk1.clone(),
160+
}
161+
)]
154162
);
155163
assert_eq!(
156164
received_txouts,
157165
vec![
158-
TxOut {
159-
value: Amount::from_sat(20_000),
160-
script_pubkey: spk2.clone(),
161-
},
162-
TxOut {
163-
value: Amount::from_sat(30_000),
164-
script_pubkey: spk1.clone(),
165-
}
166+
(
167+
0,
168+
TxOut {
169+
value: Amount::from_sat(20_000),
170+
script_pubkey: spk2.clone(),
171+
}
172+
),
173+
(
174+
1,
175+
TxOut {
176+
value: Amount::from_sat(30_000),
177+
script_pubkey: spk1.clone(),
178+
}
179+
)
166180
]
167181
);
168182

169183
let (sent_txouts, received_txouts) = index.sent_and_received_txouts(&tx2, ..1);
170184
assert_eq!(
171185
sent_txouts,
172-
vec![TxOut {
173-
value: Amount::from_sat(42_000),
174-
script_pubkey: spk1.clone(),
175-
}]
186+
vec![(
187+
0,
188+
TxOut {
189+
value: Amount::from_sat(42_000),
190+
script_pubkey: spk1.clone(),
191+
}
192+
)]
176193
);
177194
assert_eq!(
178195
received_txouts,
179-
vec![TxOut {
180-
value: Amount::from_sat(30_000),
181-
script_pubkey: spk1.clone(),
182-
}]
196+
vec![(
197+
1,
198+
TxOut {
199+
value: Amount::from_sat(30_000),
200+
script_pubkey: spk1.clone(),
201+
}
202+
)]
183203
);
184204

185205
let (sent_txouts, received_txouts) = index.sent_and_received_txouts(&tx2, 1..);
186206
assert!(sent_txouts.is_empty());
187207
assert_eq!(
188208
received_txouts,
189-
vec![TxOut {
190-
value: Amount::from_sat(20_000),
191-
script_pubkey: spk2.clone(),
192-
}]
209+
vec![(
210+
0,
211+
TxOut {
212+
value: Amount::from_sat(20_000),
213+
script_pubkey: spk2.clone(),
214+
}
215+
)]
193216
);
194217
}
195218

0 commit comments

Comments
 (0)