2024-06-28 09:07:36 -04:00
|
|
|
//! [`SpkTxOutIndex`] is an index storing [`TxOut`]s that have a script pubkey that matches those in a list.
|
|
|
|
|
2023-03-01 11:09:08 +01:00
|
|
|
use core::ops::RangeBounds;
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap},
|
2024-06-28 09:07:36 -04:00
|
|
|
Indexer,
|
2023-03-01 11:09:08 +01:00
|
|
|
};
|
2024-07-09 16:51:12 -05:00
|
|
|
use bitcoin::{Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
/// An index storing [`TxOut`]s that have a script pubkey that matches those in a list.
|
|
|
|
///
|
|
|
|
/// The basic idea is that you insert script pubkeys you care about into the index with
|
2023-08-25 12:52:09 +03:00
|
|
|
/// [`insert_spk`] and then when you call [`Indexer::index_tx`] or [`Indexer::index_txout`], the
|
|
|
|
/// index will look at any txouts you pass in and store and index any txouts matching one of its
|
|
|
|
/// script pubkeys.
|
2023-03-01 11:09:08 +01:00
|
|
|
///
|
2023-03-10 23:23:29 +05:30
|
|
|
/// Each script pubkey is associated with an application-defined index script index `I`, which must be
|
|
|
|
/// [`Ord`]. Usually, this is used to associate the derivation index of the script pubkey or even a
|
2023-03-01 11:09:08 +01:00
|
|
|
/// combination of `(keychain, derivation_index)`.
|
|
|
|
///
|
|
|
|
/// Note there is no harm in scanning transactions that disappear from the blockchain or were never
|
|
|
|
/// in there in the first place. `SpkTxOutIndex` is intentionally *monotone* -- you cannot delete or
|
|
|
|
/// modify txouts that have been indexed. To find out which txouts from the index are actually in the
|
2023-05-24 11:37:26 +08:00
|
|
|
/// chain or unspent, you must use other sources of information like a [`TxGraph`].
|
2023-03-01 11:09:08 +01:00
|
|
|
///
|
|
|
|
/// [`TxOut`]: bitcoin::TxOut
|
|
|
|
/// [`insert_spk`]: Self::insert_spk
|
|
|
|
/// [`Ord`]: core::cmp::Ord
|
2023-05-24 11:37:26 +08:00
|
|
|
/// [`TxGraph`]: crate::tx_graph::TxGraph
|
2023-03-01 11:09:08 +01:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct SpkTxOutIndex<I> {
|
|
|
|
/// script pubkeys ordered by index
|
2023-06-21 17:59:34 +02:00
|
|
|
spks: BTreeMap<I, ScriptBuf>,
|
2023-03-01 11:09:08 +01:00
|
|
|
/// A reverse lookup from spk to spk index
|
2023-06-21 17:59:34 +02:00
|
|
|
spk_indices: HashMap<ScriptBuf, I>,
|
2023-03-01 11:09:08 +01:00
|
|
|
/// The set of unused indexes.
|
|
|
|
unused: BTreeSet<I>,
|
|
|
|
/// Lookup index and txout by outpoint.
|
|
|
|
txouts: BTreeMap<OutPoint, (I, TxOut)>,
|
|
|
|
/// Lookup from spk index to outpoints that had that spk
|
|
|
|
spk_txouts: BTreeSet<(I, OutPoint)>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<I> Default for SpkTxOutIndex<I> {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
txouts: Default::default(),
|
|
|
|
spks: Default::default(),
|
|
|
|
spk_indices: Default::default(),
|
|
|
|
spk_txouts: Default::default(),
|
|
|
|
unused: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-06 10:17:55 +10:00
|
|
|
impl<I: Clone + Ord + core::fmt::Debug> Indexer for SpkTxOutIndex<I> {
|
2023-09-06 09:47:45 +03:00
|
|
|
type ChangeSet = ();
|
2023-03-26 11:24:30 +08:00
|
|
|
|
2023-08-07 17:43:17 +02:00
|
|
|
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::ChangeSet {
|
2023-09-06 09:47:45 +03:00
|
|
|
self.scan_txout(outpoint, txout);
|
|
|
|
Default::default()
|
2023-03-24 15:47:39 +08:00
|
|
|
}
|
|
|
|
|
2023-08-07 17:43:17 +02:00
|
|
|
fn index_tx(&mut self, tx: &Transaction) -> Self::ChangeSet {
|
2023-09-06 09:47:45 +03:00
|
|
|
self.scan(tx);
|
|
|
|
Default::default()
|
2023-03-24 15:47:39 +08:00
|
|
|
}
|
|
|
|
|
2023-09-06 09:47:45 +03:00
|
|
|
fn initial_changeset(&self) -> Self::ChangeSet {}
|
2023-08-16 17:39:35 +02:00
|
|
|
|
2023-08-07 17:43:17 +02:00
|
|
|
fn apply_changeset(&mut self, _changeset: Self::ChangeSet) {
|
2023-03-27 15:36:37 +08:00
|
|
|
// This applies nothing.
|
|
|
|
}
|
|
|
|
|
2023-04-05 18:17:08 +08:00
|
|
|
fn is_tx_relevant(&self, tx: &Transaction) -> bool {
|
|
|
|
self.is_relevant(tx)
|
2023-03-26 11:24:30 +08:00
|
|
|
}
|
2023-03-24 15:47:39 +08:00
|
|
|
}
|
|
|
|
|
2024-06-06 10:17:55 +10:00
|
|
|
impl<I: Clone + Ord + core::fmt::Debug> SpkTxOutIndex<I> {
|
2023-09-06 09:47:45 +03:00
|
|
|
/// Scans a transaction's outputs for matching script pubkeys.
|
|
|
|
///
|
|
|
|
/// Typically, this is used in two situations:
|
|
|
|
///
|
|
|
|
/// 1. After loading transaction data from the disk, you may scan over all the txouts to restore all
|
|
|
|
/// your txouts.
|
|
|
|
/// 2. When getting new data from the chain, you usually scan it before incorporating it into your chain state.
|
|
|
|
pub fn scan(&mut self, tx: &Transaction) -> BTreeSet<I> {
|
|
|
|
let mut scanned_indices = BTreeSet::new();
|
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(chain): use `minimal_non_dust()` instead of `dust_value()`
fix(chain): use `compute_txid()` instead of `txid`
deps(testenv): bump `electrsd` to `0.28.0`
deps(electrum): bump `electrum-client` to `0.20.0`
fix(electrum): use `compute_txid()` instead of `txid`
deps(esplora): bump `esplora-client` to `0.8.0`
deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to
`0.19.0`
fix(bitcoind_rpc): use `compute_txid()` instead of `txid`
fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected
`Signature` fields
fix(sqlite): use `compute_txid()` instead of `txid`
deps(hwi): bump `hwi` to `0.9.0`
deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(wallet): use `compute_txid()` and `minimal_non_dust()`
- update to use `compute_txid()` instead of deprecated `txid()`
- update to use `minimal_non_dust()` instead of `dust_value()`
- remove unused `bitcoin::hex::FromHex`.
fix(wallet): uses `.into` conversion on `Network` for `NetworkKind`
- uses `.into()` when appropriate, otherwise use the explicit
`NetworkKind`, and it's `.is_mainnet()` method.
fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError`
fix(wallet): fields on taproot, and ecdsa `Signature` structure
fix(wallet/wallet): convert `Weight` to `usize` for now
- converts the `bitcoin-units::Weight` type to `usize` with help of
`to_wu()` method.
- it should be updated/refactored in the future to handle the `Weight`
type throughout the code instead of current `usize`, only converting
it for now.
- allows the usage of deprecated `is_provably_unspendable()`, needs
further discussion if suggested `is_op_return` is suitable.
- update the expect field to `signature`, as it was renamed from `sig`.
fix(wallet/wallet): use `is_op_return` instead of
`is_provably_unspendable`
fix(wallet/wallet): use `relative::Locktime` instead of `Sequence`
fix(wallet/descriptor): use `ParsePublicKeyError`
fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and
`RelLockTime` to `absolute::LockTime` and `relative::LockTime`
fix(wallet/wallet): use `Message::from_digest()` instead of relying on
deprecated `ThirtyTwoByteHash` trait.
fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it
internally
fix(wallet/wallet): remove `0x` prefix from expected `TxId` display
fix(examples): use `compute_txid()` instead of `txid`
fix(ci): remove usage of `bitcoin/no-std` feature
- remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
|
|
|
let txid = tx.compute_txid();
|
2023-09-06 09:47:45 +03:00
|
|
|
for (i, txout) in tx.output.iter().enumerate() {
|
|
|
|
let op = OutPoint::new(txid, i as u32);
|
|
|
|
if let Some(spk_i) = self.scan_txout(op, txout) {
|
|
|
|
scanned_indices.insert(spk_i.clone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
scanned_indices
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Scan a single `TxOut` for a matching script pubkey and returns the index that matches the
|
|
|
|
/// script pubkey (if any).
|
|
|
|
pub fn scan_txout(&mut self, op: OutPoint, txout: &TxOut) -> Option<&I> {
|
|
|
|
let spk_i = self.spk_indices.get(&txout.script_pubkey);
|
|
|
|
if let Some(spk_i) = spk_i {
|
|
|
|
self.txouts.insert(op, (spk_i.clone(), txout.clone()));
|
|
|
|
self.spk_txouts.insert((spk_i.clone(), op));
|
|
|
|
self.unused.remove(spk_i);
|
|
|
|
}
|
|
|
|
spk_i
|
|
|
|
}
|
|
|
|
|
2023-05-10 14:14:29 +08:00
|
|
|
/// Get a reference to the set of indexed outpoints.
|
|
|
|
pub fn outpoints(&self) -> &BTreeSet<(I, OutPoint)> {
|
|
|
|
&self.spk_txouts
|
|
|
|
}
|
|
|
|
|
2023-03-01 11:09:08 +01:00
|
|
|
/// Iterate over all known txouts that spend to tracked script pubkeys.
|
|
|
|
pub fn txouts(
|
|
|
|
&self,
|
|
|
|
) -> impl DoubleEndedIterator<Item = (&I, OutPoint, &TxOut)> + ExactSizeIterator {
|
|
|
|
self.txouts
|
|
|
|
.iter()
|
|
|
|
.map(|(op, (index, txout))| (index, *op, txout))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Finds all txouts on a transaction that has previously been scanned and indexed.
|
|
|
|
pub fn txouts_in_tx(
|
|
|
|
&self,
|
|
|
|
txid: Txid,
|
|
|
|
) -> impl DoubleEndedIterator<Item = (&I, OutPoint, &TxOut)> {
|
|
|
|
self.txouts
|
|
|
|
.range(OutPoint::new(txid, u32::MIN)..=OutPoint::new(txid, u32::MAX))
|
|
|
|
.map(|(op, (index, txout))| (index, *op, txout))
|
|
|
|
}
|
|
|
|
|
2023-03-10 23:23:29 +05:30
|
|
|
/// Iterates over all the outputs with script pubkeys in an index range.
|
2023-03-01 11:09:08 +01:00
|
|
|
pub fn outputs_in_range(
|
|
|
|
&self,
|
|
|
|
range: impl RangeBounds<I>,
|
|
|
|
) -> impl DoubleEndedIterator<Item = (&I, OutPoint)> {
|
|
|
|
use bitcoin::hashes::Hash;
|
|
|
|
use core::ops::Bound::*;
|
|
|
|
let min_op = OutPoint {
|
2023-06-21 17:59:34 +02:00
|
|
|
txid: Txid::all_zeros(),
|
2023-03-01 11:09:08 +01:00
|
|
|
vout: u32::MIN,
|
|
|
|
};
|
|
|
|
let max_op = OutPoint {
|
2023-06-21 17:59:34 +02:00
|
|
|
txid: Txid::from_byte_array([0xff; Txid::LEN]),
|
2023-03-01 11:09:08 +01:00
|
|
|
vout: u32::MAX,
|
|
|
|
};
|
|
|
|
|
|
|
|
let start = match range.start_bound() {
|
|
|
|
Included(index) => Included((index.clone(), min_op)),
|
|
|
|
Excluded(index) => Excluded((index.clone(), max_op)),
|
|
|
|
Unbounded => Unbounded,
|
|
|
|
};
|
|
|
|
|
|
|
|
let end = match range.end_bound() {
|
|
|
|
Included(index) => Included((index.clone(), max_op)),
|
|
|
|
Excluded(index) => Excluded((index.clone(), min_op)),
|
|
|
|
Unbounded => Unbounded,
|
|
|
|
};
|
|
|
|
|
|
|
|
self.spk_txouts.range((start, end)).map(|(i, op)| (i, *op))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the txout and script pubkey index of the `TxOut` at `OutPoint`.
|
|
|
|
///
|
|
|
|
/// Returns `None` if the `TxOut` hasn't been scanned or if nothing matching was found there.
|
|
|
|
pub fn txout(&self, outpoint: OutPoint) -> Option<(&I, &TxOut)> {
|
2023-12-29 19:15:57 +08:00
|
|
|
self.txouts.get(&outpoint).map(|v| (&v.0, &v.1))
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the script that has been inserted at the `index`.
|
|
|
|
///
|
2023-03-10 23:23:29 +05:30
|
|
|
/// If that index hasn't been inserted yet, it will return `None`.
|
2024-07-09 16:51:12 -05:00
|
|
|
pub fn spk_at_index(&self, index: &I) -> Option<ScriptBuf> {
|
|
|
|
self.spks.get(index).cloned()
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
2023-03-10 23:23:29 +05:30
|
|
|
/// The script pubkeys that are being tracked by the index.
|
2023-06-21 17:59:34 +02:00
|
|
|
pub fn all_spks(&self) -> &BTreeMap<I, ScriptBuf> {
|
2023-03-01 11:09:08 +01:00
|
|
|
&self.spks
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds a script pubkey to scan for. Returns `false` and does nothing if spk already exists in the map
|
|
|
|
///
|
2023-03-10 23:23:29 +05:30
|
|
|
/// the index will look for outputs spending to this spk whenever it scans new data.
|
2023-06-21 17:59:34 +02:00
|
|
|
pub fn insert_spk(&mut self, index: I, spk: ScriptBuf) -> bool {
|
2023-03-01 11:09:08 +01:00
|
|
|
match self.spk_indices.entry(spk.clone()) {
|
|
|
|
Entry::Vacant(value) => {
|
|
|
|
value.insert(index.clone());
|
|
|
|
self.spks.insert(index.clone(), spk);
|
|
|
|
self.unused.insert(index);
|
|
|
|
true
|
|
|
|
}
|
|
|
|
Entry::Occupied(_) => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-10 23:23:29 +05:30
|
|
|
/// Iterates over all unused script pubkeys in an index range.
|
2023-03-01 11:09:08 +01:00
|
|
|
///
|
2023-03-10 23:23:29 +05:30
|
|
|
/// Here, "unused" means that after the script pubkey was stored in the index, the index has
|
2023-03-01 11:09:08 +01:00
|
|
|
/// never scanned a transaction output with it.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```rust
|
feat!: Rework sqlite, changesets, persistence and wallet-construction
Rework sqlite: Instead of only supported one schema (defined in
`bdk_sqlite`), we have a schema per changeset type for more flexiblity.
* rm `bdk_sqlite` crate (as we don't need `bdk_sqlite::Store` anymore).
* add `sqlite` feature on `bdk_chain` which adds methods on each
changeset type for initializing tables, loading the changeset and
writing.
Rework changesets: Some callers may want to use `KeychainTxOutIndex`
where `K` may change per descriptor on every run. So we only want to
persist the last revealed indices by `DescriptorId` (which uniquely-ish
identifies the descriptor).
* rm `keychain_added` field from `keychain_txout`'s changeset.
* Add `keychain_added` to `CombinedChangeSet` (which is renamed to
`WalletChangeSet`).
Rework persistence: add back some safety and convenience when persisting
our types. Working with changeset directly (as we were doing before) can
be cumbersome.
* Intoduce `struct Persisted<T>` which wraps a type `T` which stores
staged changes to it. This adds safety when creating and or loading
`T` from db.
* `struct Persisted<T>` methods, `create`, `load` and `persist`, are
avaliable if `trait PersistWith<Db>` is implemented for `T`. `Db`
represents the database connection and `PersistWith` should be
implemented per database-type.
* For async, we have `trait PersistedAsyncWith<Db>`.
* `Wallet` has impls of `PersistedWith<rusqlite::Connection>`,
`PersistedWith<rusqlite::Transaction>` and
`PersistedWith<bdk_file_store::Store>` by default.
Rework wallet-construction: Before, we had multiple methods for loading
and creating with different input-counts so it would be unwieldly to add
more parameters in the future. This also makes it difficult to impl
`PersistWith` (which has a single method for `load` that takes in
`PersistWith::LoadParams` and a single method for `create` that takes in
`PersistWith::CreateParams`).
* Introduce a builder pattern when constructing a `Wallet`. For loading
from persistence or `ChangeSet`, we have `LoadParams`. For creating a
new wallet, we have `CreateParams`.
2024-07-11 04:49:01 +00:00
|
|
|
/// # use bdk_chain::spk_txout::SpkTxOutIndex;
|
2023-03-01 11:09:08 +01:00
|
|
|
///
|
|
|
|
/// // imagine our spks are indexed like (keychain, derivation_index).
|
2023-03-02 11:12:13 +01:00
|
|
|
/// let txout_index = SpkTxOutIndex::<(u32, u32)>::default();
|
2023-03-01 11:09:08 +01:00
|
|
|
/// let all_unused_spks = txout_index.unused_spks(..);
|
|
|
|
/// let change_index = 1;
|
2023-03-02 11:12:13 +01:00
|
|
|
/// let unused_change_spks =
|
|
|
|
/// txout_index.unused_spks((change_index, u32::MIN)..(change_index, u32::MAX));
|
2023-03-01 11:09:08 +01:00
|
|
|
/// ```
|
2024-07-09 16:51:12 -05:00
|
|
|
pub fn unused_spks<R>(
|
|
|
|
&self,
|
|
|
|
range: R,
|
|
|
|
) -> impl DoubleEndedIterator<Item = (&I, ScriptBuf)> + Clone + '_
|
2023-03-01 11:09:08 +01:00
|
|
|
where
|
|
|
|
R: RangeBounds<I>,
|
|
|
|
{
|
|
|
|
self.unused
|
|
|
|
.range(range)
|
2023-03-02 22:05:11 -06:00
|
|
|
.map(move |index| (index, self.spk_at_index(index).expect("must exist")))
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns whether the script pubkey at `index` has been used or not.
|
|
|
|
///
|
2023-03-10 23:23:29 +05:30
|
|
|
/// Here, "unused" means that after the script pubkey was stored in the index, the index has
|
2023-03-01 11:09:08 +01:00
|
|
|
/// never scanned a transaction output with it.
|
|
|
|
pub fn is_used(&self, index: &I) -> bool {
|
2024-05-02 14:53:37 +02:00
|
|
|
!self.unused.contains(index)
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
2023-03-10 23:23:29 +05:30
|
|
|
/// Marks the script pubkey at `index` as used even though it hasn't seen an output spending to it.
|
|
|
|
/// This only affects when the `index` had already been added to `self` and was unused.
|
2023-03-01 11:09:08 +01:00
|
|
|
///
|
2023-03-10 23:23:29 +05:30
|
|
|
/// Returns whether the `index` was initially present as `unused`.
|
2023-03-01 11:09:08 +01:00
|
|
|
///
|
|
|
|
/// This is useful when you want to reserve a script pubkey for something but don't want to add
|
2023-03-10 23:23:29 +05:30
|
|
|
/// the transaction output using it to the index yet. Other callers will consider the `index` used
|
2023-03-01 11:09:08 +01:00
|
|
|
/// until you call [`unmark_used`].
|
|
|
|
///
|
|
|
|
/// [`unmark_used`]: Self::unmark_used
|
|
|
|
pub fn mark_used(&mut self, index: &I) -> bool {
|
|
|
|
self.unused.remove(index)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Undoes the effect of [`mark_used`]. Returns whether the `index` is inserted back into
|
|
|
|
/// `unused`.
|
|
|
|
///
|
|
|
|
/// Note that if `self` has scanned an output with this script pubkey then this will have no
|
|
|
|
/// effect.
|
|
|
|
///
|
|
|
|
/// [`mark_used`]: Self::mark_used
|
|
|
|
pub fn unmark_used(&mut self, index: &I) -> bool {
|
2023-03-10 23:23:29 +05:30
|
|
|
// we cannot set the index as unused when it does not exist
|
2023-03-01 11:09:08 +01:00
|
|
|
if !self.spks.contains_key(index) {
|
|
|
|
return false;
|
|
|
|
}
|
2023-03-10 23:23:29 +05:30
|
|
|
// we cannot set the index as unused when txouts are indexed under it
|
2023-03-01 11:09:08 +01:00
|
|
|
if self.outputs_in_range(index..=index).next().is_some() {
|
|
|
|
return false;
|
|
|
|
}
|
2023-03-02 19:08:33 +01:00
|
|
|
self.unused.insert(index.clone())
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the index associated with the script pubkey.
|
2024-07-09 16:51:12 -05:00
|
|
|
pub fn index_of_spk(&self, script: ScriptBuf) -> Option<&I> {
|
|
|
|
self.spk_indices.get(script.as_script())
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
2024-02-06 17:31:22 +11:00
|
|
|
/// Computes the total value transfer effect `tx` has on the script pubkeys in `range`. Value is
|
|
|
|
/// *sent* when a script pubkey in the `range` is on an input and *received* when it is on an
|
|
|
|
/// output. For `sent` to be computed correctly, the output being spent must have already been
|
|
|
|
/// scanned by the index. Calculating received just uses the [`Transaction`] outputs directly,
|
|
|
|
/// so it will be correct even if it has not been scanned.
|
2024-04-26 11:17:23 -03:00
|
|
|
pub fn sent_and_received(
|
|
|
|
&self,
|
|
|
|
tx: &Transaction,
|
|
|
|
range: impl RangeBounds<I>,
|
|
|
|
) -> (Amount, Amount) {
|
|
|
|
let mut sent = Amount::ZERO;
|
|
|
|
let mut received = Amount::ZERO;
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
for txin in &tx.input {
|
2024-02-06 17:31:22 +11:00
|
|
|
if let Some((index, txout)) = self.txout(txin.previous_output) {
|
|
|
|
if range.contains(index) {
|
2024-04-26 11:17:23 -03:00
|
|
|
sent += txout.value;
|
2024-02-06 17:31:22 +11:00
|
|
|
}
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for txout in &tx.output {
|
2024-07-09 16:51:12 -05:00
|
|
|
if let Some(index) = self.index_of_spk(txout.script_pubkey.clone()) {
|
2024-02-06 17:31:22 +11:00
|
|
|
if range.contains(index) {
|
2024-04-26 11:17:23 -03:00
|
|
|
received += txout.value;
|
2024-02-06 17:31:22 +11:00
|
|
|
}
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
(sent, received)
|
|
|
|
}
|
|
|
|
|
2024-02-06 17:31:22 +11:00
|
|
|
/// Computes the net value transfer effect of `tx` on the script pubkeys in `range`. Shorthand
|
|
|
|
/// for calling [`sent_and_received`] and subtracting sent from received.
|
2023-03-01 11:09:08 +01:00
|
|
|
///
|
|
|
|
/// [`sent_and_received`]: Self::sent_and_received
|
2024-04-26 11:17:23 -03:00
|
|
|
pub fn net_value(&self, tx: &Transaction, range: impl RangeBounds<I>) -> SignedAmount {
|
2024-02-06 17:31:22 +11:00
|
|
|
let (sent, received) = self.sent_and_received(tx, range);
|
2024-04-26 11:17:23 -03:00
|
|
|
received.to_signed().expect("valid `SignedAmount`")
|
|
|
|
- sent.to_signed().expect("valid `SignedAmount`")
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Whether any of the inputs of this transaction spend a txout tracked or whether any output
|
|
|
|
/// matches one of our script pubkeys.
|
|
|
|
///
|
|
|
|
/// It is easily possible to misuse this method and get false negatives by calling it before you
|
2023-03-10 23:23:29 +05:30
|
|
|
/// have scanned the `TxOut`s the transaction is spending. For example, if you want to filter out
|
|
|
|
/// all the transactions in a block that are irrelevant, you **must first scan all the
|
2023-03-01 11:09:08 +01:00
|
|
|
/// transactions in the block** and only then use this method.
|
|
|
|
pub fn is_relevant(&self, tx: &Transaction) -> bool {
|
|
|
|
let input_matches = tx
|
|
|
|
.input
|
|
|
|
.iter()
|
2023-03-02 19:08:33 +01:00
|
|
|
.any(|input| self.txouts.contains_key(&input.previous_output));
|
2023-03-01 11:09:08 +01:00
|
|
|
let output_matches = tx
|
|
|
|
.output
|
|
|
|
.iter()
|
2023-03-02 19:08:33 +01:00
|
|
|
.any(|output| self.spk_indices.contains_key(&output.script_pubkey));
|
2023-03-01 11:09:08 +01:00
|
|
|
input_matches || output_matches
|
|
|
|
}
|
|
|
|
}
|