Skip to content

Commit 61276fe

Browse files
committed
refactor(tx_graph): add pub changeset module
- The original `tx_graph::ChangeSet` is moved to `changeset` module. - The indexed changeset (formerly `indexed_tx_graph::ChangeSet`) is now defined in `changeset::indexed` module. This is the type used by `TxGraph`.
1 parent c6047a0 commit 61276fe

File tree

10 files changed

+272
-203
lines changed

10 files changed

+272
-203
lines changed

crates/bitcoind_rpc/tests/test_emitter.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ fn test_into_tx_graph() -> anyhow::Result<()> {
189189
assert!(emitter.next_block()?.is_none());
190190

191191
let mempool_txs = emitter.mempool()?;
192-
let tx_graph_changeset = tx_graph.batch_insert_unconfirmed(mempool_txs);
192+
let tx_graph_changeset = tx_graph.batch_insert_unconfirmed(mempool_txs).tx_graph;
193193
assert_eq!(
194194
tx_graph_changeset
195195
.txs
@@ -221,7 +221,9 @@ fn test_into_tx_graph() -> anyhow::Result<()> {
221221
let emission = emitter.next_block()?.expect("must get mined block");
222222
let height = emission.block_height();
223223
let _ = chain.apply_update(emission.checkpoint)?;
224-
let tx_graph_changeset = tx_graph.apply_block_relevant(&emission.block, height);
224+
let tx_graph_changeset = tx_graph
225+
.apply_block_relevant(&emission.block, height)
226+
.tx_graph;
225227
assert!(tx_graph_changeset.txs.is_empty());
226228
assert!(tx_graph_changeset.txouts.is_empty());
227229
assert_eq!(tx_graph_changeset.anchors, exp_anchors);

crates/chain/src/rusqlite_impl.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use rusqlite::named_params;
1616
use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
1717
use rusqlite::OptionalExtension;
1818
use rusqlite::Transaction;
19+
use tx_graph::changeset as tx;
1920

2021
/// Table name for schemas.
2122
pub const SCHEMAS_TABLE_NAME: &str = "bdk_schemas";
@@ -199,8 +200,8 @@ fn to_sql_error<E: std::error::Error + Send + Sync + 'static>(err: E) -> rusqlit
199200
rusqlite::Error::ToSqlConversionFailure(Box::new(err))
200201
}
201202

202-
impl tx_graph::ChangeSet<ConfirmationBlockTime> {
203-
/// Schema name for [`tx_graph::ChangeSet`].
203+
impl tx::ChangeSet<ConfirmationBlockTime> {
204+
/// Schema name for [`tx::ChangeSet`].
204205
pub const SCHEMA_NAME: &'static str = "bdk_txgraph";
205206
/// Name of table that stores full transactions and `last_seen` timestamps.
206207
pub const TXS_TABLE_NAME: &'static str = "bdk_txs";
@@ -209,7 +210,7 @@ impl tx_graph::ChangeSet<ConfirmationBlockTime> {
209210
/// Name of table that stores [`Anchor`]s.
210211
pub const ANCHORS_TABLE_NAME: &'static str = "bdk_anchors";
211212

212-
/// Get v0 of sqlite [tx_graph::ChangeSet] schema
213+
/// Get v0 of sqlite [`tx::ChangeSet`] schema
213214
pub fn schema_v0() -> String {
214215
// full transactions
215216
let create_txs_table = format!(
@@ -247,7 +248,7 @@ impl tx_graph::ChangeSet<ConfirmationBlockTime> {
247248
format!("{create_txs_table}; {create_txouts_table}; {create_anchors_table}")
248249
}
249250

250-
/// Get v1 of sqlite [tx_graph::ChangeSet] schema
251+
/// Get v1 of sqlite [`tx::ChangeSet`] schema
251252
pub fn schema_v1() -> String {
252253
let add_confirmation_time_column = format!(
253254
"ALTER TABLE {} ADD COLUMN confirmation_time INTEGER DEFAULT -1 NOT NULL",
@@ -567,7 +568,7 @@ mod test {
567568

568569
#[test]
569570
fn can_persist_anchors_and_txs_independently() -> anyhow::Result<()> {
570-
type ChangeSet = tx_graph::ChangeSet<ConfirmationBlockTime>;
571+
type ChangeSet = tx::ChangeSet<ConfirmationBlockTime>;
571572
let mut conn = rusqlite::Connection::open_in_memory()?;
572573

573574
// init tables
@@ -629,7 +630,7 @@ mod test {
629630

630631
#[test]
631632
fn v0_to_v1_schema_migration_is_backward_compatible() -> anyhow::Result<()> {
632-
type ChangeSet = tx_graph::ChangeSet<ConfirmationBlockTime>;
633+
type ChangeSet = tx::ChangeSet<ConfirmationBlockTime>;
633634
let mut conn = rusqlite::Connection::open_in_memory()?;
634635

635636
// Create initial database with v0 sqlite schema

crates/chain/src/tx_graph.rs

Lines changed: 14 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,15 @@ use alloc::vec::Vec;
113113
use bdk_core::ConfirmationBlockTime;
114114
pub use bdk_core::TxUpdate;
115115
use bitcoin::{Amount, Block, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
116+
use changeset::{self as tx, indexed::ChangeSet};
116117
use core::fmt::{self, Formatter};
117118
use core::{
118119
convert::Infallible,
119120
ops::{Deref, RangeInclusive},
120121
};
121122

123+
pub mod changeset;
124+
122125
impl<A: Ord, X> From<TxGraph<A, X>> for TxUpdate<A> {
123126
fn from(graph: TxGraph<A, X>) -> Self {
124127
Self {
@@ -631,7 +634,7 @@ impl<A: Anchor, X: Indexer> TxGraph<A, X> {
631634
);
632635
}
633636
None => {
634-
changeset.txouts.insert(outpoint, txout);
637+
changeset.tx_graph.txouts.insert(outpoint, txout);
635638
}
636639
}
637640
}
@@ -669,7 +672,7 @@ impl<A: Anchor, X: Indexer> TxGraph<A, X> {
669672
.insert(txid);
670673
}
671674
*partial_tx = TxNodeInternal::Whole(tx.clone());
672-
changeset.txs.insert(tx);
675+
changeset.tx_graph.txs.insert(tx);
673676
}
674677
}
675678

@@ -830,7 +833,7 @@ impl<A: Anchor, X: Indexer> TxGraph<A, X> {
830833
}
831834
self.txs_by_highest_conf_heights.insert((new_top_h, txid));
832835
}
833-
changeset.anchors.insert((anchor, txid));
836+
changeset.tx_graph.anchors.insert((anchor, txid));
834837
}
835838
changeset
836839
}
@@ -862,7 +865,7 @@ impl<A: Anchor, X: Indexer> TxGraph<A, X> {
862865
self.txs_by_last_seen.remove(&(old_last_seen, txid));
863866
}
864867
self.txs_by_last_seen.insert((seen_at, txid));
865-
changeset.last_seen.insert(txid, seen_at);
868+
changeset.tx_graph.last_seen.insert(txid, seen_at);
866869
}
867870
changeset
868871
}
@@ -970,25 +973,28 @@ impl<A: Anchor, X: Indexer> TxGraph<A, X> {
970973

971974
/// Determines the [`ChangeSet`] between `self` and an empty [`TxGraph`].
972975
pub fn initial_changeset(&self) -> ChangeSet<A, X::ChangeSet> {
973-
ChangeSet {
976+
let tx_graph = tx::ChangeSet {
974977
txs: self.full_txs().map(|tx_node| tx_node.tx).collect(),
975978
txouts: self
976979
.floating_txouts()
977980
.map(|(op, txout)| (op, txout.clone()))
978981
.collect(),
979982
anchors: self.rev_anchors(),
980983
last_seen: self.last_seen.clone().into_iter().collect(),
984+
};
985+
ChangeSet {
986+
tx_graph,
981987
indexer: self.index.initial_changeset(),
982988
}
983989
}
984990

985991
/// Indexes the txs and txouts of `changeset` and merges the resulting changes.
986992
fn index_changeset(&mut self, changeset: &mut ChangeSet<A, X::ChangeSet>) {
987993
let indexer = &mut changeset.indexer;
988-
for tx in &changeset.txs {
994+
for tx in &changeset.tx_graph.txs {
989995
indexer.merge(self.index.index_tx(tx));
990996
}
991-
for (&outpoint, txout) in &changeset.txouts {
997+
for (&outpoint, txout) in &changeset.tx_graph.txouts {
992998
indexer.merge(self.index.index_txout(outpoint, txout));
993999
}
9941000
}
@@ -999,6 +1005,7 @@ impl<A: Anchor, X: Indexer> TxGraph<A, X> {
9991005
// transactions so we do that first.
10001006
self.index.apply_changeset(changeset.indexer);
10011007

1008+
let changeset = changeset.tx_graph;
10021009
for tx in &changeset.txs {
10031010
self.index.index_tx(tx);
10041011
}
@@ -1397,142 +1404,6 @@ where
13971404
}
13981405
}
13991406

1400-
/// The [`ChangeSet`] represents changes to a [`TxGraph`].
1401-
///
1402-
/// Since [`TxGraph`] is monotone, the "changeset" can only contain transactions to be added and
1403-
/// not removed.
1404-
///
1405-
/// Refer to [module-level documentation](self) for more.
1406-
#[derive(Debug, Clone, PartialEq)]
1407-
#[cfg_attr(
1408-
feature = "serde",
1409-
derive(serde::Deserialize, serde::Serialize),
1410-
serde(bound(
1411-
deserialize = "A: Ord + serde::Deserialize<'de>, X: serde::Deserialize<'de>",
1412-
serialize = "A: Ord + serde::Serialize, X: serde::Serialize",
1413-
))
1414-
)]
1415-
#[must_use]
1416-
pub struct ChangeSet<A = (), X = ()> {
1417-
/// Added transactions.
1418-
pub txs: BTreeSet<Arc<Transaction>>,
1419-
/// Added txouts.
1420-
pub txouts: BTreeMap<OutPoint, TxOut>,
1421-
/// Added anchors.
1422-
pub anchors: BTreeSet<(A, Txid)>,
1423-
/// Added last-seen unix timestamps of transactions.
1424-
pub last_seen: BTreeMap<Txid, u64>,
1425-
/// [`Indexer`] changes
1426-
pub indexer: X,
1427-
}
1428-
1429-
impl<A, X: Default> Default for ChangeSet<A, X> {
1430-
fn default() -> Self {
1431-
Self {
1432-
txs: Default::default(),
1433-
txouts: Default::default(),
1434-
anchors: Default::default(),
1435-
last_seen: Default::default(),
1436-
indexer: Default::default(),
1437-
}
1438-
}
1439-
}
1440-
1441-
impl<A, X> From<X> for ChangeSet<A, X> {
1442-
fn from(indexer: X) -> Self {
1443-
Self {
1444-
indexer,
1445-
txs: Default::default(),
1446-
txouts: Default::default(),
1447-
anchors: Default::default(),
1448-
last_seen: Default::default(),
1449-
}
1450-
}
1451-
}
1452-
1453-
impl<A, X> ChangeSet<A, X> {
1454-
/// Iterates over all outpoints contained within [`ChangeSet`].
1455-
pub fn txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
1456-
self.txs
1457-
.iter()
1458-
.flat_map(|tx| {
1459-
tx.output
1460-
.iter()
1461-
.enumerate()
1462-
.map(move |(vout, txout)| (OutPoint::new(tx.compute_txid(), vout as _), txout))
1463-
})
1464-
.chain(self.txouts.iter().map(|(op, txout)| (*op, txout)))
1465-
}
1466-
1467-
/// Iterates over the heights of that the new transaction anchors in this changeset.
1468-
///
1469-
/// This is useful if you want to find which heights you need to fetch data about in order to
1470-
/// confirm or exclude these anchors.
1471-
pub fn anchor_heights(&self) -> impl Iterator<Item = u32> + '_
1472-
where
1473-
A: Anchor,
1474-
{
1475-
let mut dedup = None;
1476-
self.anchors
1477-
.iter()
1478-
.map(|(a, _)| a.anchor_block().height)
1479-
.filter(move |height| {
1480-
let duplicate = dedup == Some(*height);
1481-
dedup = Some(*height);
1482-
!duplicate
1483-
})
1484-
}
1485-
}
1486-
1487-
impl<A: Ord, X: Merge> Merge for ChangeSet<A, X> {
1488-
fn merge(&mut self, other: Self) {
1489-
// We use `extend` instead of `BTreeMap::append` due to performance issues with `append`.
1490-
// Refer to https://github.com/rust-lang/rust/issues/34666#issuecomment-675658420
1491-
self.txs.extend(other.txs);
1492-
self.txouts.extend(other.txouts);
1493-
self.anchors.extend(other.anchors);
1494-
1495-
// last_seen timestamps should only increase
1496-
self.last_seen.extend(
1497-
other
1498-
.last_seen
1499-
.into_iter()
1500-
.filter(|(txid, update_ls)| self.last_seen.get(txid) < Some(update_ls))
1501-
.collect::<Vec<_>>(),
1502-
);
1503-
self.indexer.merge(other.indexer);
1504-
}
1505-
1506-
fn is_empty(&self) -> bool {
1507-
self.txs.is_empty()
1508-
&& self.txouts.is_empty()
1509-
&& self.anchors.is_empty()
1510-
&& self.last_seen.is_empty()
1511-
&& self.indexer.is_empty()
1512-
}
1513-
}
1514-
1515-
impl<A: Ord, X> ChangeSet<A, X> {
1516-
/// Transform the [`ChangeSet`] to have [`Anchor`]s of another type.
1517-
///
1518-
/// This takes in a closure of signature `FnMut(A) -> A2` which is called for each [`Anchor`] to
1519-
/// transform it.
1520-
pub fn map_anchors<A2: Ord, F>(self, mut f: F) -> ChangeSet<A2, X>
1521-
where
1522-
F: FnMut(A) -> A2,
1523-
{
1524-
ChangeSet {
1525-
txs: self.txs,
1526-
txouts: self.txouts,
1527-
anchors: BTreeSet::<(A2, Txid)>::from_iter(
1528-
self.anchors.into_iter().map(|(a, txid)| (f(a), txid)),
1529-
),
1530-
last_seen: self.last_seen,
1531-
indexer: self.indexer,
1532-
}
1533-
}
1534-
}
1535-
15361407
impl<A, X> AsRef<TxGraph<A, X>> for TxGraph<A, X> {
15371408
fn as_ref(&self) -> &TxGraph<A, X> {
15381409
self

0 commit comments

Comments
 (0)