[bdk_chain_redesign] Make default anchor for TxGraph
as ()
This allows us to use the old API with minimal changes. `TxGraph` methods have also been rearranged to allow for it.
This commit is contained in:
parent
468701a129
commit
8c906170c9
@ -524,7 +524,7 @@ impl<D> Wallet<D> {
|
||||
/// unconfirmed transactions last.
|
||||
pub fn transactions(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (ConfirmationTime, TxInGraph<'_, Transaction, BlockId>)> + '_
|
||||
) -> impl DoubleEndedIterator<Item = (ConfirmationTime, TxInGraph<'_, Transaction, ()>)> + '_
|
||||
{
|
||||
self.keychain_tracker
|
||||
.chain_graph()
|
||||
|
@ -27,7 +27,7 @@ use core::fmt::Debug;
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct ChainGraph<P = TxHeight> {
|
||||
chain: SparseChain<P>,
|
||||
graph: TxGraph<BlockId>,
|
||||
graph: TxGraph,
|
||||
}
|
||||
|
||||
impl<P> Default for ChainGraph<P> {
|
||||
@ -45,8 +45,8 @@ impl<P> AsRef<SparseChain<P>> for ChainGraph<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P> AsRef<TxGraph<BlockId>> for ChainGraph<P> {
|
||||
fn as_ref(&self) -> &TxGraph<BlockId> {
|
||||
impl<P> AsRef<TxGraph> for ChainGraph<P> {
|
||||
fn as_ref(&self) -> &TxGraph {
|
||||
&self.graph
|
||||
}
|
||||
}
|
||||
@ -64,7 +64,7 @@ impl<P> ChainGraph<P> {
|
||||
}
|
||||
|
||||
/// Returns a reference to the internal [`TxGraph`].
|
||||
pub fn graph(&self) -> &TxGraph<BlockId> {
|
||||
pub fn graph(&self) -> &TxGraph {
|
||||
&self.graph
|
||||
}
|
||||
}
|
||||
@ -81,7 +81,7 @@ where
|
||||
/// transaction in `graph`.
|
||||
/// 2. The `chain` has two transactions that are allegedly in it, but they conflict in the `graph`
|
||||
/// (so could not possibly be in the same chain).
|
||||
pub fn new(chain: SparseChain<P>, graph: TxGraph<BlockId>) -> Result<Self, NewError<P>> {
|
||||
pub fn new(chain: SparseChain<P>, graph: TxGraph) -> Result<Self, NewError<P>> {
|
||||
let mut missing = HashSet::default();
|
||||
for (pos, txid) in chain.txids() {
|
||||
if let Some(graphed_tx) = graph.get_tx(*txid) {
|
||||
@ -212,7 +212,7 @@ where
|
||||
///
|
||||
/// This does not necessarily mean that it is *confirmed* in the blockchain; it might just be in
|
||||
/// the unconfirmed transaction list within the [`SparseChain`].
|
||||
pub fn get_tx_in_chain(&self, txid: Txid) -> Option<(&P, TxInGraph<'_, Transaction, BlockId>)> {
|
||||
pub fn get_tx_in_chain(&self, txid: Txid) -> Option<(&P, TxInGraph<'_, Transaction, ()>)> {
|
||||
let position = self.chain.tx_position(txid)?;
|
||||
let graphed_tx = self.graph.get_tx(txid).expect("must exist");
|
||||
Some((position, graphed_tx))
|
||||
@ -430,7 +430,7 @@ where
|
||||
/// in ascending order.
|
||||
pub fn transactions_in_chain(
|
||||
&self,
|
||||
) -> impl DoubleEndedIterator<Item = (&P, TxInGraph<'_, Transaction, BlockId>)> {
|
||||
) -> impl DoubleEndedIterator<Item = (&P, TxInGraph<'_, Transaction, ()>)> {
|
||||
self.chain
|
||||
.txids()
|
||||
.map(move |(pos, txid)| (pos, self.graph.get_tx(*txid).expect("must exist")))
|
||||
@ -469,7 +469,7 @@ where
|
||||
#[must_use]
|
||||
pub struct ChangeSet<P> {
|
||||
pub chain: sparse_chain::ChangeSet<P>,
|
||||
pub graph: tx_graph::Additions<BlockId>,
|
||||
pub graph: tx_graph::Additions<()>,
|
||||
}
|
||||
|
||||
impl<P> ChangeSet<P> {
|
||||
|
@ -55,7 +55,7 @@
|
||||
//! assert!(additions.is_empty());
|
||||
//! ```
|
||||
|
||||
use crate::{collections::*, BlockAnchor, BlockId, ChainOracle, ForEachTxOut, ObservedIn};
|
||||
use crate::{collections::*, BlockAnchor, ChainOracle, ForEachTxOut, ObservedIn};
|
||||
use alloc::vec::Vec;
|
||||
use bitcoin::{OutPoint, Transaction, TxOut, Txid};
|
||||
use core::{
|
||||
@ -69,7 +69,7 @@ use core::{
|
||||
///
|
||||
/// [module-level documentation]: crate::tx_graph
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct TxGraph<A = BlockId> {
|
||||
pub struct TxGraph<A = ()> {
|
||||
// all transactions that the graph is aware of in format: `(tx_node, tx_anchors, tx_last_seen)`
|
||||
txs: HashMap<Txid, (TxNode, BTreeSet<A>, u64)>,
|
||||
spends: BTreeMap<OutPoint, HashSet<Txid>>,
|
||||
@ -244,9 +244,111 @@ impl<A> TxGraph<A> {
|
||||
|
||||
Some(inputs_sum - outputs_sum)
|
||||
}
|
||||
|
||||
/// The transactions spending from this output.
|
||||
///
|
||||
/// `TxGraph` allows conflicting transactions within the graph. Obviously the transactions in
|
||||
/// the returned set will never be in the same active-chain.
|
||||
pub fn outspends(&self, outpoint: OutPoint) -> &HashSet<Txid> {
|
||||
self.spends.get(&outpoint).unwrap_or(&self.empty_outspends)
|
||||
}
|
||||
|
||||
/// Iterates over the transactions spending from `txid`.
|
||||
///
|
||||
/// The iterator item is a union of `(vout, txid-set)` where:
|
||||
///
|
||||
/// - `vout` is the provided `txid`'s outpoint that is being spent
|
||||
/// - `txid-set` is the set of txids spending the `vout`.
|
||||
pub fn tx_outspends(
|
||||
&self,
|
||||
txid: Txid,
|
||||
) -> impl DoubleEndedIterator<Item = (u32, &HashSet<Txid>)> + '_ {
|
||||
let start = OutPoint { txid, vout: 0 };
|
||||
let end = OutPoint {
|
||||
txid,
|
||||
vout: u32::MAX,
|
||||
};
|
||||
self.spends
|
||||
.range(start..=end)
|
||||
.map(|(outpoint, spends)| (outpoint.vout, spends))
|
||||
}
|
||||
|
||||
/// Iterate over all partial transactions (outputs only) in the graph.
|
||||
pub fn partial_transactions(
|
||||
&self,
|
||||
) -> impl Iterator<Item = TxInGraph<'_, BTreeMap<u32, TxOut>, A>> {
|
||||
self.txs
|
||||
.iter()
|
||||
.filter_map(|(&txid, (tx, anchors, last_seen))| match tx {
|
||||
TxNode::Whole(_) => None,
|
||||
TxNode::Partial(partial) => Some(TxInGraph {
|
||||
txid,
|
||||
tx: partial,
|
||||
anchors,
|
||||
last_seen: *last_seen,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates an iterator that filters and maps descendants from the starting `txid`.
|
||||
///
|
||||
/// The supplied closure takes in two inputs `(depth, descendant_txid)`:
|
||||
///
|
||||
/// * `depth` is the distance between the starting `txid` and the `descendant_txid`. I.e., if the
|
||||
/// descendant is spending an output of the starting `txid`; the `depth` will be 1.
|
||||
/// * `descendant_txid` is the descendant's txid which we are considering to walk.
|
||||
///
|
||||
/// The supplied closure returns an `Option<T>`, allowing the caller to map each node it vists
|
||||
/// and decide whether to visit descendants.
|
||||
pub fn walk_descendants<'g, F, O>(&'g self, txid: Txid, walk_map: F) -> TxDescendants<A, F>
|
||||
where
|
||||
F: FnMut(usize, Txid) -> Option<O> + 'g,
|
||||
{
|
||||
TxDescendants::new_exclude_root(self, txid, walk_map)
|
||||
}
|
||||
|
||||
/// Creates an iterator that both filters and maps conflicting transactions (this includes
|
||||
/// descendants of directly-conflicting transactions, which are also considered conflicts).
|
||||
///
|
||||
/// Refer to [`Self::walk_descendants`] for `walk_map` usage.
|
||||
pub fn walk_conflicts<'g, F, O>(
|
||||
&'g self,
|
||||
tx: &'g Transaction,
|
||||
walk_map: F,
|
||||
) -> TxDescendants<A, F>
|
||||
where
|
||||
F: FnMut(usize, Txid) -> Option<O> + 'g,
|
||||
{
|
||||
let txids = self.direct_conflicts_of_tx(tx).map(|(_, txid)| txid);
|
||||
TxDescendants::from_multiple_include_root(self, txids, walk_map)
|
||||
}
|
||||
|
||||
/// Given a transaction, return an iterator of txids that directly conflict with the given
|
||||
/// transaction's inputs (spends). The conflicting txids are returned with the given
|
||||
/// transaction's vin (in which it conflicts).
|
||||
///
|
||||
/// Note that this only returns directly conflicting txids and does not include descendants of
|
||||
/// those txids (which are technically also conflicting).
|
||||
pub fn direct_conflicts_of_tx<'g>(
|
||||
&'g self,
|
||||
tx: &'g Transaction,
|
||||
) -> impl Iterator<Item = (usize, Txid)> + '_ {
|
||||
let txid = tx.txid();
|
||||
tx.input
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(move |(vin, txin)| self.spends.get(&txin.previous_output).zip(Some(vin)))
|
||||
.flat_map(|(spends, vin)| core::iter::repeat(vin).zip(spends.iter().cloned()))
|
||||
.filter(move |(_, conflicting_txid)| *conflicting_txid != txid)
|
||||
}
|
||||
|
||||
/// Whether the graph has any transactions or outputs in it.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.txs.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: BlockAnchor> TxGraph<A> {
|
||||
impl<A: Clone + Ord> TxGraph<A> {
|
||||
/// Construct a new [`TxGraph`] from a list of transactions.
|
||||
pub fn new(txs: impl IntoIterator<Item = Transaction>) -> Self {
|
||||
let mut new = Self::default();
|
||||
@ -256,6 +358,24 @@ impl<A: BlockAnchor> TxGraph<A> {
|
||||
new
|
||||
}
|
||||
|
||||
/// Returns the resultant [`Additions`] if the given `txout` is inserted at `outpoint`. Does not
|
||||
/// mutate `self`.
|
||||
///
|
||||
/// The [`Additions`] result will be empty if the `outpoint` (or a full transaction containing
|
||||
/// the `outpoint`) already existed in `self`.
|
||||
pub fn insert_txout_preview(&self, outpoint: OutPoint, txout: TxOut) -> Additions<A> {
|
||||
let mut update = Self::default();
|
||||
update.txs.insert(
|
||||
outpoint.txid,
|
||||
(
|
||||
TxNode::Partial([(outpoint.vout, txout)].into()),
|
||||
BTreeSet::new(),
|
||||
0,
|
||||
),
|
||||
);
|
||||
self.determine_additions(&update)
|
||||
}
|
||||
|
||||
/// Inserts the given [`TxOut`] at [`OutPoint`].
|
||||
///
|
||||
/// Note this will ignore the action if we already have the full transaction that the txout is
|
||||
@ -266,6 +386,18 @@ impl<A: BlockAnchor> TxGraph<A> {
|
||||
additions
|
||||
}
|
||||
|
||||
/// Returns the resultant [`Additions`] if the given transaction is inserted. Does not actually
|
||||
/// mutate [`Self`].
|
||||
///
|
||||
/// The [`Additions`] result will be empty if `tx` already exists in `self`.
|
||||
pub fn insert_tx_preview(&self, tx: Transaction) -> Additions<A> {
|
||||
let mut update = Self::default();
|
||||
update
|
||||
.txs
|
||||
.insert(tx.txid(), (TxNode::Whole(tx), BTreeSet::new(), 0));
|
||||
self.determine_additions(&update)
|
||||
}
|
||||
|
||||
/// Inserts the given transaction into [`TxGraph`].
|
||||
///
|
||||
/// The [`Additions`] returned will be empty if `tx` already exists.
|
||||
@ -275,6 +407,13 @@ impl<A: BlockAnchor> TxGraph<A> {
|
||||
additions
|
||||
}
|
||||
|
||||
/// Returns the resultant [`Additions`] if the `txid` is set in `anchor`.
|
||||
pub fn insert_anchor_preview(&self, txid: Txid, anchor: A) -> Additions<A> {
|
||||
let mut update = Self::default();
|
||||
update.anchors.insert((anchor, txid));
|
||||
self.determine_additions(&update)
|
||||
}
|
||||
|
||||
/// Inserts the given `anchor` into [`TxGraph`].
|
||||
///
|
||||
/// This is equivalent to calling [`insert_anchor_preview`] and [`apply_additions`] in sequence.
|
||||
@ -289,6 +428,16 @@ impl<A: BlockAnchor> TxGraph<A> {
|
||||
additions
|
||||
}
|
||||
|
||||
/// Returns the resultant [`Additions`] if the `txid` is set to `seen_at`.
|
||||
///
|
||||
/// Note that [`TxGraph`] only keeps track of the lastest `seen_at`.
|
||||
pub fn insert_seen_at_preview(&self, txid: Txid, seen_at: u64) -> Additions<A> {
|
||||
let mut update = Self::default();
|
||||
let (_, _, update_last_seen) = update.txs.entry(txid).or_default();
|
||||
*update_last_seen = seen_at;
|
||||
self.determine_additions(&update)
|
||||
}
|
||||
|
||||
/// Inserts the given `seen_at` into [`TxGraph`].
|
||||
///
|
||||
/// This is equivalent to calling [`insert_seen_at_preview`] and [`apply_additions`] in
|
||||
@ -421,54 +570,9 @@ impl<A: BlockAnchor> TxGraph<A> {
|
||||
|
||||
additions
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the resultant [`Additions`] if the given transaction is inserted. Does not actually
|
||||
/// mutate [`Self`].
|
||||
///
|
||||
/// The [`Additions`] result will be empty if `tx` already exists in `self`.
|
||||
pub fn insert_tx_preview(&self, tx: Transaction) -> Additions<A> {
|
||||
let mut update = Self::default();
|
||||
update
|
||||
.txs
|
||||
.insert(tx.txid(), (TxNode::Whole(tx), BTreeSet::new(), 0));
|
||||
self.determine_additions(&update)
|
||||
}
|
||||
|
||||
/// Returns the resultant [`Additions`] if the given `txout` is inserted at `outpoint`. Does not
|
||||
/// mutate `self`.
|
||||
///
|
||||
/// The [`Additions`] result will be empty if the `outpoint` (or a full transaction containing
|
||||
/// the `outpoint`) already existed in `self`.
|
||||
pub fn insert_txout_preview(&self, outpoint: OutPoint, txout: TxOut) -> Additions<A> {
|
||||
let mut update = Self::default();
|
||||
update.txs.insert(
|
||||
outpoint.txid,
|
||||
(
|
||||
TxNode::Partial([(outpoint.vout, txout)].into()),
|
||||
BTreeSet::new(),
|
||||
0,
|
||||
),
|
||||
);
|
||||
self.determine_additions(&update)
|
||||
}
|
||||
|
||||
/// Returns the resultant [`Additions`] if the `txid` is set in `anchor`.
|
||||
pub fn insert_anchor_preview(&self, txid: Txid, anchor: A) -> Additions<A> {
|
||||
let mut update = Self::default();
|
||||
update.anchors.insert((anchor, txid));
|
||||
self.determine_additions(&update)
|
||||
}
|
||||
|
||||
/// Returns the resultant [`Additions`] if the `txid` is set to `seen_at`.
|
||||
///
|
||||
/// Note that [`TxGraph`] only keeps track of the lastest `seen_at`.
|
||||
pub fn insert_seen_at_preview(&self, txid: Txid, seen_at: u64) -> Additions<A> {
|
||||
let mut update = Self::default();
|
||||
let (_, _, update_last_seen) = update.txs.entry(txid).or_default();
|
||||
*update_last_seen = seen_at;
|
||||
self.determine_additions(&update)
|
||||
}
|
||||
|
||||
impl<A: BlockAnchor> TxGraph<A> {
|
||||
/// Get all heights that are relevant to the graph.
|
||||
pub fn relevant_heights(&self) -> BTreeSet<u32> {
|
||||
self.anchors
|
||||
@ -573,110 +677,6 @@ impl<A: BlockAnchor> TxGraph<A> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<A> TxGraph<A> {
|
||||
/// The transactions spending from this output.
|
||||
///
|
||||
/// `TxGraph` allows conflicting transactions within the graph. Obviously the transactions in
|
||||
/// the returned set will never be in the same active-chain.
|
||||
pub fn outspends(&self, outpoint: OutPoint) -> &HashSet<Txid> {
|
||||
self.spends.get(&outpoint).unwrap_or(&self.empty_outspends)
|
||||
}
|
||||
|
||||
/// Iterates over the transactions spending from `txid`.
|
||||
///
|
||||
/// The iterator item is a union of `(vout, txid-set)` where:
|
||||
///
|
||||
/// - `vout` is the provided `txid`'s outpoint that is being spent
|
||||
/// - `txid-set` is the set of txids spending the `vout`.
|
||||
pub fn tx_outspends(
|
||||
&self,
|
||||
txid: Txid,
|
||||
) -> impl DoubleEndedIterator<Item = (u32, &HashSet<Txid>)> + '_ {
|
||||
let start = OutPoint { txid, vout: 0 };
|
||||
let end = OutPoint {
|
||||
txid,
|
||||
vout: u32::MAX,
|
||||
};
|
||||
self.spends
|
||||
.range(start..=end)
|
||||
.map(|(outpoint, spends)| (outpoint.vout, spends))
|
||||
}
|
||||
|
||||
/// Iterate over all partial transactions (outputs only) in the graph.
|
||||
pub fn partial_transactions(
|
||||
&self,
|
||||
) -> impl Iterator<Item = TxInGraph<'_, BTreeMap<u32, TxOut>, A>> {
|
||||
self.txs
|
||||
.iter()
|
||||
.filter_map(|(&txid, (tx, anchors, last_seen))| match tx {
|
||||
TxNode::Whole(_) => None,
|
||||
TxNode::Partial(partial) => Some(TxInGraph {
|
||||
txid,
|
||||
tx: partial,
|
||||
anchors,
|
||||
last_seen: *last_seen,
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates an iterator that filters and maps descendants from the starting `txid`.
|
||||
///
|
||||
/// The supplied closure takes in two inputs `(depth, descendant_txid)`:
|
||||
///
|
||||
/// * `depth` is the distance between the starting `txid` and the `descendant_txid`. I.e., if the
|
||||
/// descendant is spending an output of the starting `txid`; the `depth` will be 1.
|
||||
/// * `descendant_txid` is the descendant's txid which we are considering to walk.
|
||||
///
|
||||
/// The supplied closure returns an `Option<T>`, allowing the caller to map each node it vists
|
||||
/// and decide whether to visit descendants.
|
||||
pub fn walk_descendants<'g, F, O>(&'g self, txid: Txid, walk_map: F) -> TxDescendants<A, F>
|
||||
where
|
||||
F: FnMut(usize, Txid) -> Option<O> + 'g,
|
||||
{
|
||||
TxDescendants::new_exclude_root(self, txid, walk_map)
|
||||
}
|
||||
|
||||
/// Creates an iterator that both filters and maps conflicting transactions (this includes
|
||||
/// descendants of directly-conflicting transactions, which are also considered conflicts).
|
||||
///
|
||||
/// Refer to [`Self::walk_descendants`] for `walk_map` usage.
|
||||
pub fn walk_conflicts<'g, F, O>(
|
||||
&'g self,
|
||||
tx: &'g Transaction,
|
||||
walk_map: F,
|
||||
) -> TxDescendants<A, F>
|
||||
where
|
||||
F: FnMut(usize, Txid) -> Option<O> + 'g,
|
||||
{
|
||||
let txids = self.direct_conflicts_of_tx(tx).map(|(_, txid)| txid);
|
||||
TxDescendants::from_multiple_include_root(self, txids, walk_map)
|
||||
}
|
||||
|
||||
/// Given a transaction, return an iterator of txids that directly conflict with the given
|
||||
/// transaction's inputs (spends). The conflicting txids are returned with the given
|
||||
/// transaction's vin (in which it conflicts).
|
||||
///
|
||||
/// Note that this only returns directly conflicting txids and does not include descendants of
|
||||
/// those txids (which are technically also conflicting).
|
||||
pub fn direct_conflicts_of_tx<'g>(
|
||||
&'g self,
|
||||
tx: &'g Transaction,
|
||||
) -> impl Iterator<Item = (usize, Txid)> + '_ {
|
||||
let txid = tx.txid();
|
||||
tx.input
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(move |(vin, txin)| self.spends.get(&txin.previous_output).zip(Some(vin)))
|
||||
.flat_map(|(spends, vin)| core::iter::repeat(vin).zip(spends.iter().cloned()))
|
||||
.filter(move |(_, conflicting_txid)| *conflicting_txid != txid)
|
||||
}
|
||||
|
||||
/// Whether the graph has any transactions or outputs in it.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.txs.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
/// A structure that represents changes to a [`TxGraph`].
|
||||
///
|
||||
/// It is named "additions" because [`TxGraph`] is monotone, so transactions can only be added and
|
||||
@ -698,7 +698,7 @@ impl<A> TxGraph<A> {
|
||||
)
|
||||
)]
|
||||
#[must_use]
|
||||
pub struct Additions<A = BlockId> {
|
||||
pub struct Additions<A> {
|
||||
pub tx: BTreeSet<Transaction>,
|
||||
pub txout: BTreeMap<OutPoint, TxOut>,
|
||||
pub anchors: BTreeSet<(A, Txid)>,
|
||||
|
Loading…
x
Reference in New Issue
Block a user