[bdk_chain_redesign] Fix tx_graph::Additions::append logic

* `Additions` now implements `Append` and uses `Append` to implement
  `append()`.
* `append()` logic enforces that `last_seen` values should only
  increase.
* Test written for `append()` with `last_seen` behaviour.
This commit is contained in:
志宇 2023-04-28 18:54:36 +08:00
parent f101dde09b
commit e536307e5c
No known key found for this signature in database
GPG Key ID: F6345C9837C2BDE8
3 changed files with 49 additions and 22 deletions

View File

@ -3,7 +3,7 @@ use crate::{
collections::HashSet, collections::HashSet,
sparse_chain::{self, ChainPosition, SparseChain}, sparse_chain::{self, ChainPosition, SparseChain},
tx_graph::{self, TxGraph}, tx_graph::{self, TxGraph},
BlockId, ForEachTxOut, FullTxOut, TxHeight, Append, BlockId, ForEachTxOut, FullTxOut, TxHeight,
}; };
use alloc::{string::ToString, vec::Vec}; use alloc::{string::ToString, vec::Vec};
use bitcoin::{OutPoint, Transaction, TxOut, Txid}; use bitcoin::{OutPoint, Transaction, TxOut, Txid};

View File

@ -55,7 +55,9 @@
//! assert!(additions.is_empty()); //! assert!(additions.is_empty());
//! ``` //! ```
use crate::{collections::*, Anchor, BlockId, ChainOracle, ForEachTxOut, FullTxOut, ObservedAs}; use crate::{
collections::*, Anchor, Append, BlockId, ChainOracle, ForEachTxOut, FullTxOut, ObservedAs,
};
use alloc::vec::Vec; use alloc::vec::Vec;
use bitcoin::{OutPoint, Transaction, TxOut, Txid}; use bitcoin::{OutPoint, Transaction, TxOut, Txid};
use core::{ use core::{
@ -112,17 +114,6 @@ impl<'a, T, A> Deref for TxNode<'a, T, A> {
} }
} }
impl<'a, A> TxNode<'a, Transaction, A> {
pub fn from_tx(tx: &'a Transaction, anchors: &'a BTreeSet<A>) -> Self {
Self {
txid: tx.txid(),
tx,
anchors,
last_seen_unconfirmed: 0,
}
}
}
/// Internal representation of a transaction node of a [`TxGraph`]. /// Internal representation of a transaction node of a [`TxGraph`].
/// ///
/// This can either be a whole transaction, or a partial transaction (where we only have select /// This can either be a whole transaction, or a partial transaction (where we only have select
@ -602,7 +593,7 @@ impl<A: Clone + Ord> TxGraph<A> {
impl<A: Anchor> TxGraph<A> { impl<A: Anchor> TxGraph<A> {
/// Get all heights that are relevant to the graph. /// Get all heights that are relevant to the graph.
pub fn relevant_heights(&self) -> impl DoubleEndedIterator<Item = u32> + '_ { pub fn relevant_heights(&self) -> impl Iterator<Item = u32> + '_ {
let mut last_height = Option::<u32>::None; let mut last_height = Option::<u32>::None;
self.anchors self.anchors
.iter() .iter()
@ -944,17 +935,22 @@ impl<A> Additions<A> {
}) })
.chain(self.txout.iter().map(|(op, txout)| (*op, txout))) .chain(self.txout.iter().map(|(op, txout)| (*op, txout)))
} }
}
/// Appends the changes in `other` into self such that applying `self` afterward has the same impl<A: Ord> Append for Additions<A> {
/// effect as sequentially applying the original `self` and `other`. fn append(&mut self, mut other: Self) {
pub fn append(&mut self, mut other: Additions<A>)
where
A: Ord,
{
self.tx.append(&mut other.tx); self.tx.append(&mut other.tx);
self.txout.append(&mut other.txout); self.txout.append(&mut other.txout);
self.anchors.append(&mut other.anchors); self.anchors.append(&mut other.anchors);
self.last_seen.append(&mut other.last_seen);
// last_seen timestamps should only increase
self.last_seen.extend(
other
.last_seen
.into_iter()
.filter(|(txid, update_ls)| self.last_seen.get(txid) < Some(update_ls))
.collect::<Vec<_>>(),
);
} }
} }

View File

@ -4,7 +4,7 @@ use bdk_chain::{
collections::*, collections::*,
local_chain::LocalChain, local_chain::LocalChain,
tx_graph::{Additions, TxGraph}, tx_graph::{Additions, TxGraph},
BlockId, ObservedAs, Append, BlockId, ObservedAs,
}; };
use bitcoin::{ use bitcoin::{
hashes::Hash, BlockHash, OutPoint, PackedLockTime, Script, Transaction, TxIn, TxOut, Txid, hashes::Hash, BlockHash, OutPoint, PackedLockTime, Script, Transaction, TxIn, TxOut, Txid,
@ -849,3 +849,34 @@ fn test_relevant_heights() {
"anchor for non-existant tx is inserted at height 5, must still be in relevant heights", "anchor for non-existant tx is inserted at height 5, must still be in relevant heights",
); );
} }
/// Ensure that `last_seen` values only increase during [`Append::append`].
#[test]
fn test_additions_last_seen_append() {
let txid: Txid = h!("test txid");
let test_cases: &[(Option<u64>, Option<u64>)] = &[
(Some(5), Some(6)),
(Some(5), Some(5)),
(Some(6), Some(5)),
(None, Some(5)),
(Some(5), None),
];
for (original_ls, update_ls) in test_cases {
let mut original = Additions::<()> {
last_seen: original_ls.map(|ls| (txid, ls)).into_iter().collect(),
..Default::default()
};
let update = Additions::<()> {
last_seen: update_ls.map(|ls| (txid, ls)).into_iter().collect(),
..Default::default()
};
original.append(update);
assert_eq!(
&original.last_seen.get(&txid).cloned(),
Ord::max(original_ls, update_ls),
);
}
}