feat(tx_graph): Add method update_last_seen_unconfirmed
That accepts a `u64` as param representing the latest timestamp and internally calls `insert_seen_at` for all transactions in graph that aren't yet anchored in a confirmed block.
This commit is contained in:
parent
b5557dce70
commit
37fca35dde
@ -541,7 +541,11 @@ impl<A: Clone + Ord> TxGraph<A> {
|
||||
|
||||
/// Inserts the given `seen_at` for `txid` into [`TxGraph`].
|
||||
///
|
||||
/// Note that [`TxGraph`] only keeps track of the latest `seen_at`.
|
||||
/// Note that [`TxGraph`] only keeps track of the latest `seen_at`. To batch
|
||||
/// update all unconfirmed transactions with the latest `seen_at`, see
|
||||
/// [`update_last_seen_unconfirmed`].
|
||||
///
|
||||
/// [`update_last_seen_unconfirmed`]: Self::update_last_seen_unconfirmed
|
||||
pub fn insert_seen_at(&mut self, txid: Txid, seen_at: u64) -> ChangeSet<A> {
|
||||
let mut update = Self::default();
|
||||
let (_, _, update_last_seen) = update.txs.entry(txid).or_default();
|
||||
@ -549,6 +553,65 @@ impl<A: Clone + Ord> TxGraph<A> {
|
||||
self.apply_update(update)
|
||||
}
|
||||
|
||||
/// Update the last seen time for all unconfirmed transactions.
|
||||
///
|
||||
/// This method updates the last seen unconfirmed time for this [`TxGraph`] by inserting
|
||||
/// the given `seen_at` for every transaction not yet anchored to a confirmed block,
|
||||
/// and returns the [`ChangeSet`] after applying all updates to `self`.
|
||||
///
|
||||
/// This is useful for keeping track of the latest time a transaction was seen
|
||||
/// unconfirmed, which is important for evaluating transaction conflicts in the same
|
||||
/// [`TxGraph`]. For details of how [`TxGraph`] resolves conflicts, see the docs for
|
||||
/// [`try_get_chain_position`].
|
||||
///
|
||||
/// A normal use of this method is to call it with the current system time. Although
|
||||
/// block headers contain a timestamp, using the header time would be less effective
|
||||
/// at tracking mempool transactions, because it can drift from actual clock time, plus
|
||||
/// we may want to update a transaction's last seen time repeatedly between blocks.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// # use bdk_chain::example_utils::*;
|
||||
/// # use std::time::UNIX_EPOCH;
|
||||
/// # let tx = tx_from_hex(RAW_TX_1);
|
||||
/// # let mut tx_graph = bdk_chain::TxGraph::<()>::new([tx]);
|
||||
/// let now = std::time::SystemTime::now()
|
||||
/// .duration_since(UNIX_EPOCH)
|
||||
/// .expect("valid duration")
|
||||
/// .as_secs();
|
||||
/// let changeset = tx_graph.update_last_seen_unconfirmed(now);
|
||||
/// assert!(!changeset.last_seen.is_empty());
|
||||
/// ```
|
||||
///
|
||||
/// Note that [`TxGraph`] only keeps track of the latest `seen_at`, so the given time must
|
||||
/// by strictly greater than what is currently stored for a transaction to have an effect.
|
||||
/// To insert a last seen time for a single txid, see [`insert_seen_at`].
|
||||
///
|
||||
/// [`insert_seen_at`]: Self::insert_seen_at
|
||||
/// [`try_get_chain_position`]: Self::try_get_chain_position
|
||||
pub fn update_last_seen_unconfirmed(&mut self, seen_at: u64) -> ChangeSet<A> {
|
||||
let mut changeset = ChangeSet::default();
|
||||
let unanchored_txs: Vec<Txid> = self
|
||||
.txs
|
||||
.iter()
|
||||
.filter_map(
|
||||
|(&txid, (_, anchors, _))| {
|
||||
if anchors.is_empty() {
|
||||
Some(txid)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
)
|
||||
.collect();
|
||||
|
||||
for txid in unanchored_txs {
|
||||
changeset.append(self.insert_seen_at(txid, seen_at));
|
||||
}
|
||||
changeset
|
||||
}
|
||||
|
||||
/// Extends this graph with another so that `self` becomes the union of the two sets of
|
||||
/// transactions.
|
||||
///
|
||||
|
@ -1048,6 +1048,34 @@ fn test_changeset_last_seen_append() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn update_last_seen_unconfirmed() {
|
||||
let mut graph = TxGraph::<()>::default();
|
||||
let tx = new_tx(0);
|
||||
let txid = tx.txid();
|
||||
|
||||
// insert a new tx
|
||||
// initially we have a last_seen of 0, and no anchors
|
||||
let _ = graph.insert_tx(tx);
|
||||
let tx = graph.full_txs().next().unwrap();
|
||||
assert_eq!(tx.last_seen_unconfirmed, 0);
|
||||
assert!(tx.anchors.is_empty());
|
||||
|
||||
// higher timestamp should update last seen
|
||||
let changeset = graph.update_last_seen_unconfirmed(2);
|
||||
assert_eq!(changeset.last_seen.get(&txid).unwrap(), &2);
|
||||
|
||||
// lower timestamp has no effect
|
||||
let changeset = graph.update_last_seen_unconfirmed(1);
|
||||
assert!(changeset.last_seen.is_empty());
|
||||
|
||||
// once anchored, last seen is not updated
|
||||
let _ = graph.insert_anchor(txid, ());
|
||||
let changeset = graph.update_last_seen_unconfirmed(4);
|
||||
assert!(changeset.is_empty());
|
||||
assert_eq!(graph.full_txs().next().unwrap().last_seen_unconfirmed, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_missing_blocks() {
|
||||
/// An anchor implementation for testing, made up of `(the_anchor_block, random_data)`.
|
||||
|
Loading…
x
Reference in New Issue
Block a user