Skip to content

Commit 0873d17

Browse files
committed
tx_graph: maintain v1 ChangeSet
1 parent c6047a0 commit 0873d17

File tree

5 files changed

+122
-25
lines changed

5 files changed

+122
-25
lines changed

crates/chain/src/rusqlite_impl.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -199,8 +199,8 @@ fn to_sql_error<E: std::error::Error + Send + Sync + 'static>(err: E) -> rusqlit
199199
rusqlite::Error::ToSqlConversionFailure(Box::new(err))
200200
}
201201

202-
impl tx_graph::ChangeSet<ConfirmationBlockTime> {
203-
/// Schema name for [`tx_graph::ChangeSet`].
202+
impl tx_graph::v1::ChangeSet<ConfirmationBlockTime> {
203+
/// Schema name for [`tx_graph::v1::ChangeSet`].
204204
pub const SCHEMA_NAME: &'static str = "bdk_txgraph";
205205
/// Name of table that stores full transactions and `last_seen` timestamps.
206206
pub const TXS_TABLE_NAME: &'static str = "bdk_txs";
@@ -209,7 +209,7 @@ impl tx_graph::ChangeSet<ConfirmationBlockTime> {
209209
/// Name of table that stores [`Anchor`]s.
210210
pub const ANCHORS_TABLE_NAME: &'static str = "bdk_anchors";
211211

212-
/// Get v0 of sqlite [tx_graph::ChangeSet] schema
212+
/// Get v0 of sqlite [tx_graph::v1::ChangeSet] schema
213213
pub fn schema_v0() -> String {
214214
// full transactions
215215
let create_txs_table = format!(
@@ -247,7 +247,7 @@ impl tx_graph::ChangeSet<ConfirmationBlockTime> {
247247
format!("{create_txs_table}; {create_txouts_table}; {create_anchors_table}")
248248
}
249249

250-
/// Get v1 of sqlite [tx_graph::ChangeSet] schema
250+
/// Get v1 of sqlite [tx_graph::v1::ChangeSet] schema
251251
pub fn schema_v1() -> String {
252252
let add_confirmation_time_column = format!(
253253
"ALTER TABLE {} ADD COLUMN confirmation_time INTEGER DEFAULT -1 NOT NULL",
@@ -567,7 +567,7 @@ mod test {
567567

568568
#[test]
569569
fn can_persist_anchors_and_txs_independently() -> anyhow::Result<()> {
570-
type ChangeSet = tx_graph::ChangeSet<ConfirmationBlockTime>;
570+
type ChangeSet = tx_graph::v1::ChangeSet<ConfirmationBlockTime>;
571571
let mut conn = rusqlite::Connection::open_in_memory()?;
572572

573573
// init tables
@@ -629,7 +629,7 @@ mod test {
629629

630630
#[test]
631631
fn v0_to_v1_schema_migration_is_backward_compatible() -> anyhow::Result<()> {
632-
type ChangeSet = tx_graph::ChangeSet<ConfirmationBlockTime>;
632+
type ChangeSet = tx_graph::v1::ChangeSet<ConfirmationBlockTime>;
633633
let mut conn = rusqlite::Connection::open_in_memory()?;
634634

635635
// Create initial database with v0 sqlite schema

crates/chain/src/tx_graph.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ use core::{
119119
ops::{Deref, RangeInclusive},
120120
};
121121

122+
mod changeset;
123+
pub use changeset::v1;
124+
122125
impl<A: Ord, X> From<TxGraph<A, X>> for TxUpdate<A> {
123126
fn from(graph: TxGraph<A, X>) -> Self {
124127
Self {
@@ -1450,6 +1453,18 @@ impl<A, X> From<X> for ChangeSet<A, X> {
14501453
}
14511454
}
14521455

1456+
impl<A> From<v1::ChangeSet<A>> for ChangeSet<A> {
1457+
fn from(cs: v1::ChangeSet<A>) -> Self {
1458+
Self {
1459+
txs: cs.txs,
1460+
txouts: cs.txouts,
1461+
anchors: cs.anchors,
1462+
last_seen: cs.last_seen,
1463+
indexer: (),
1464+
}
1465+
}
1466+
}
1467+
14531468
impl<A, X> ChangeSet<A, X> {
14541469
/// Iterates over all outpoints contained within [`ChangeSet`].
14551470
pub fn txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
@@ -1482,6 +1497,19 @@ impl<A, X> ChangeSet<A, X> {
14821497
!duplicate
14831498
})
14841499
}
1500+
1501+
/// Downgrade this changeset to [`v1`] and return a tuple that includes the `indexer` changes.
1502+
pub fn to_v1(self) -> (v1::ChangeSet<A>, X) {
1503+
(
1504+
v1::ChangeSet {
1505+
txs: self.txs,
1506+
txouts: self.txouts,
1507+
anchors: self.anchors,
1508+
last_seen: self.last_seen,
1509+
},
1510+
self.indexer,
1511+
)
1512+
}
14851513
}
14861514

14871515
impl<A: Ord, X: Merge> Merge for ChangeSet<A, X> {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use alloc::sync::Arc;
2+
use alloc::vec::Vec;
3+
4+
use bitcoin::{OutPoint, Transaction, TxOut, Txid};
5+
6+
use crate::collections::{BTreeMap, BTreeSet};
7+
use crate::Merge;
8+
9+
/// Version 1 [`ChangeSet`](v1::ChangeSet)
10+
pub mod v1 {
11+
use super::*;
12+
13+
/// The [`ChangeSet`] represents changes to a [`TxGraph`].
14+
///
15+
/// Since [`TxGraph`] is monotone, the "changeset" can only contain transactions to be added and
16+
/// not removed.
17+
///
18+
/// Refer to the [module-level documentation](crate::tx_graph) for more.
19+
///
20+
/// [`TxGraph`]: crate::TxGraph
21+
#[derive(Debug, Clone, PartialEq)]
22+
#[cfg_attr(
23+
feature = "serde",
24+
derive(serde::Deserialize, serde::Serialize),
25+
serde(bound(
26+
deserialize = "A: Ord + serde::Deserialize<'de>",
27+
serialize = "A: Ord + serde::Serialize",
28+
))
29+
)]
30+
pub struct ChangeSet<A = ()> {
31+
/// Added transactions.
32+
pub txs: BTreeSet<Arc<Transaction>>,
33+
/// Added txouts.
34+
pub txouts: BTreeMap<OutPoint, TxOut>,
35+
/// Added anchors.
36+
pub anchors: BTreeSet<(A, Txid)>,
37+
/// Added last-seen unix timestamps of transactions.
38+
pub last_seen: BTreeMap<Txid, u64>,
39+
}
40+
41+
impl<A> Default for ChangeSet<A> {
42+
fn default() -> Self {
43+
Self {
44+
txs: Default::default(),
45+
txouts: Default::default(),
46+
anchors: Default::default(),
47+
last_seen: Default::default(),
48+
}
49+
}
50+
}
51+
52+
impl<A: Ord> Merge for ChangeSet<A> {
53+
fn merge(&mut self, other: Self) {
54+
// We use `extend` instead of `BTreeMap::append` due to performance issues with `append`.
55+
// Refer to https://github.com/rust-lang/rust/issues/34666#issuecomment-675658420
56+
self.txs.extend(other.txs);
57+
self.txouts.extend(other.txouts);
58+
self.anchors.extend(other.anchors);
59+
60+
// last_seen timestamps should only increase
61+
self.last_seen.extend(
62+
other
63+
.last_seen
64+
.into_iter()
65+
.filter(|(txid, update_ls)| self.last_seen.get(txid) < Some(update_ls))
66+
.collect::<Vec<_>>(),
67+
);
68+
}
69+
70+
fn is_empty(&self) -> bool {
71+
self.txs.is_empty()
72+
&& self.txouts.is_empty()
73+
&& self.anchors.is_empty()
74+
&& self.last_seen.is_empty()
75+
}
76+
}
77+
}

crates/wallet/src/wallet/changeset.rs

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,7 @@ pub struct ChangeSet {
1313
/// Changes to the [`LocalChain`](local_chain::LocalChain).
1414
pub local_chain: local_chain::ChangeSet,
1515
/// Changes to [`TxGraph`](tx_graph::TxGraph).
16-
///
17-
/// The `indexer` field of this changeset can be ignored, as it is always the unit
18-
/// type. The actual [`Self::indexer`] changeset is managed separately.
19-
pub tx_graph: tx_graph::ChangeSet<ConfirmationBlockTime>,
16+
pub tx_graph: tx_graph::v1::ChangeSet<ConfirmationBlockTime>,
2017
/// Changes to [`KeychainTxOutIndex`](keychain_txout::KeychainTxOutIndex).
2118
pub indexer: keychain_txout::ChangeSet,
2219
}
@@ -90,9 +87,9 @@ impl ChangeSet {
9087
&[&Self::schema_v0()],
9188
)?;
9289

93-
bdk_chain::local_chain::ChangeSet::init_sqlite_tables(db_tx)?;
94-
bdk_chain::tx_graph::ChangeSet::<ConfirmationBlockTime>::init_sqlite_tables(db_tx)?;
95-
bdk_chain::keychain_txout::ChangeSet::init_sqlite_tables(db_tx)?;
90+
local_chain::ChangeSet::init_sqlite_tables(db_tx)?;
91+
tx_graph::v1::ChangeSet::<ConfirmationBlockTime>::init_sqlite_tables(db_tx)?;
92+
keychain_txout::ChangeSet::init_sqlite_tables(db_tx)?;
9693

9794
Ok(())
9895
}
@@ -126,7 +123,7 @@ impl ChangeSet {
126123
}
127124

128125
changeset.local_chain = local_chain::ChangeSet::from_sqlite(db_tx)?;
129-
changeset.tx_graph = tx_graph::ChangeSet::<_>::from_sqlite(db_tx)?;
126+
changeset.tx_graph = tx_graph::v1::ChangeSet::<_>::from_sqlite(db_tx)?;
130127
changeset.indexer = keychain_txout::ChangeSet::from_sqlite(db_tx)?;
131128

132129
Ok(changeset)
@@ -193,22 +190,17 @@ impl From<tx_graph::ChangeSet<ConfirmationBlockTime, keychain_txout::ChangeSet>>
193190
fn from(
194191
tx_graph: tx_graph::ChangeSet<ConfirmationBlockTime, keychain_txout::ChangeSet>,
195192
) -> Self {
193+
let (tx_graph, indexer) = tx_graph.to_v1();
196194
Self {
197-
tx_graph: tx_graph::ChangeSet {
198-
txs: tx_graph.txs,
199-
txouts: tx_graph.txouts,
200-
anchors: tx_graph.anchors,
201-
last_seen: tx_graph.last_seen,
202-
..Default::default()
203-
},
204-
indexer: tx_graph.indexer,
195+
tx_graph,
196+
indexer,
205197
..Default::default()
206198
}
207199
}
208200
}
209201

210-
impl From<tx_graph::ChangeSet<ConfirmationBlockTime>> for ChangeSet {
211-
fn from(tx_graph: tx_graph::ChangeSet<ConfirmationBlockTime>) -> Self {
202+
impl From<tx_graph::v1::ChangeSet<ConfirmationBlockTime>> for ChangeSet {
203+
fn from(tx_graph: tx_graph::v1::ChangeSet<ConfirmationBlockTime>) -> Self {
212204
Self {
213205
tx_graph,
214206
..Default::default()

crates/wallet/src/wallet/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ impl Wallet {
600600
index.apply_changeset(changeset.indexer);
601601

602602
let mut graph = TxGraph::new(());
603-
graph.apply_changeset(changeset.tx_graph);
603+
graph.apply_changeset(changeset.tx_graph.into());
604604
let mut indexed_graph = graph.swap_indexer(index).0;
605605
let _ = indexed_graph.reindex();
606606

0 commit comments

Comments
 (0)