From 26dcc9144244cd36688e678d4108806913c08910 Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Sun, 3 May 2026 11:29:37 -0500 Subject: [PATCH 1/2] deps: update wallet to 3.0, add locked_outpoints persistence --- Cargo.toml | 4 +-- src/lib.rs | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 9c71487..a8872e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ readme = "README.md" rust-version = "1.85.0" [dependencies] -bdk_wallet = {version = "2.0.0", optional = true} -bdk_chain = {version = "0.23.0", features = ["serde"]} +bdk_wallet = {version = "3", optional = true} +bdk_chain = {version = "0.23", features = ["serde"]} ciborium = "0.2.2" redb = "2.5.0" thiserror = "2.0.12" diff --git a/src/lib.rs b/src/lib.rs index 82a1bca..d4bc3c6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -127,6 +127,7 @@ pub struct Store { last_evicted_table_name: String, first_seen_table_name: String, spk_table_name: String, + locked_outpoints_table_name: String, } impl Store { @@ -187,6 +188,12 @@ impl Store { TableDefinition::new(&self.spk_table_name) } + // This table stores (OutPoint, locked) pairs on a high level. + // where OutPoint = (Txid, vout) and locked is a boolean + fn locked_outpoints_table_defn(&self) -> TableDefinition<'_, ([u8; 32], u32), bool> { + TableDefinition::new(&self.locked_outpoints_table_name) + } + /// This function creates a brand new [`Store`]. /// /// [`Store`]: crate::Store @@ -212,6 +219,8 @@ impl Store { last_revealed_table_name.push_str("_last_revealed"); let mut spk_table_name = wallet_name.clone(); spk_table_name.push_str("_spk"); + let mut locked_outpoints_table_name = wallet_name.clone(); + locked_outpoints_table_name.push_str("_locked_outpoints"); Ok(Store { db, wallet_name, @@ -225,6 +234,7 @@ impl Store { first_seen_table_name, last_revealed_table_name, spk_table_name, + locked_outpoints_table_name, }) } @@ -237,6 +247,7 @@ impl Store { let _ = write_tx.open_table(NETWORK)?; let _ = write_tx.open_table(self.keychains_table_defn())?; + let _ = write_tx.open_table(self.locked_outpoints_table_defn())?; write_tx.commit()?; self.create_local_chain_tables()?; @@ -323,6 +334,7 @@ impl Store { self.persist_local_chain(&changeset.local_chain)?; self.persist_indexer(&changeset.indexer)?; self.persist_tx_graph::(&changeset.tx_graph)?; + self.persist_locked_outpoints(&changeset.locked_outpoints)?; Ok(()) } @@ -357,6 +369,28 @@ impl Store { Ok(()) } + #[cfg(feature = "wallet")] + /// This function persists the locked outpoints into our db. + pub fn persist_locked_outpoints( + &self, + changeset: &bdk_wallet::locked_outpoints::ChangeSet, + ) -> Result<(), StoreError> { + let write_tx = self.db.begin_write()?; + { + let mut table = write_tx.open_table(self.locked_outpoints_table_defn())?; + for (outpoint, locked) in &changeset.outpoints { + if *locked { + table.insert((outpoint.txid.to_byte_array(), outpoint.vout), locked)?; + } else { + // remove the outpoint if locked is false + table.remove((outpoint.txid.to_byte_array(), outpoint.vout))?; + } + } + } + write_tx.commit()?; + Ok(()) + } + /// This function persists the descriptors into our db. pub fn persist_keychains( &self, @@ -564,6 +598,7 @@ impl Store { self.read_local_chain(&mut changeset.local_chain)?; self.read_tx_graph::(&mut changeset.tx_graph)?; self.read_indexer(&mut changeset.indexer)?; + self.read_locked_outpoints_changeset(&mut changeset.locked_outpoints)?; Ok(()) } @@ -600,6 +635,17 @@ impl Store { Ok(()) } + #[cfg(feature = "wallet")] + /// This function loads the locked outpoints from our db. + pub fn read_locked_outpoints_changeset( + &self, + changeset: &mut bdk_wallet::locked_outpoints::ChangeSet, + ) -> Result<(), StoreError> { + let read_tx = self.db.begin_read()?; + self.read_locked_outpoints(&read_tx, &mut changeset.outpoints)?; + Ok(()) + } + /// This function loads descriptors from db. pub fn read_keychains( &self, @@ -815,6 +861,28 @@ impl Store { } Ok(()) } + + #[cfg(feature = "wallet")] + // This function loads locked_outpoints corresponding to the wallet. + fn read_locked_outpoints( + &self, + read_tx: &ReadTransaction, + outpoints: &mut BTreeMap, + ) -> Result<(), StoreError> { + let table = read_tx.open_table(self.locked_outpoints_table_defn())?; + + for entry in table.iter()? { + let (outpoint, locked) = entry?; + outpoints.insert( + OutPoint { + txid: Txid::from_byte_array(outpoint.value().0), + vout: outpoint.value().1, + }, + locked.value(), + ); + } + Ok(()) + } } #[cfg(feature = "wallet")] @@ -1093,7 +1161,7 @@ mod test { store .read_last_seen(&read_tx, &mut last_seen_read_new) .unwrap(); - last_seen.merge(last_seen_new); + Merge::merge(&mut last_seen, last_seen_new); assert_eq!(last_seen_read_new, last_seen); } @@ -1167,7 +1235,7 @@ mod test { store .read_last_evicted(&read_tx, &mut last_evicted_read_new) .unwrap(); - last_evicted.merge(last_evicted_new); + Merge::merge(&mut last_evicted, last_evicted_new); assert_eq!(last_evicted_read_new, last_evicted); } @@ -1243,7 +1311,7 @@ mod test { store .read_first_seen(&read_tx, &mut first_seen_read_new) .unwrap(); - first_seen.merge(first_seen_new); + Merge::merge(&mut first_seen, first_seen_new); assert_eq!(first_seen_read_new, first_seen); } @@ -1322,7 +1390,7 @@ mod test { let mut txouts_read_new: BTreeMap = BTreeMap::new(); store.read_txouts(&read_tx, &mut txouts_read_new).unwrap(); - txouts.merge(txouts_new); + Merge::merge(&mut txouts, txouts_new); assert_eq!(txouts, txouts_read_new); } @@ -1611,7 +1679,7 @@ mod test { .read_last_revealed(&read_tx, &mut last_revealed_read_new) .unwrap(); - last_revealed.merge(last_revealed_new); + Merge::merge(&mut last_revealed, last_revealed_new); assert_eq!(last_revealed, last_revealed_read_new); } @@ -1746,6 +1814,8 @@ mod test { #[cfg(feature = "wallet")] #[test] fn test_persist_wallet() { + use bdk_wallet::locked_outpoints; + let tmpfile = NamedTempFile::new().unwrap(); let db = Arc::new(create_db(tmpfile.path())); let store = create_test_store(db, "wallet1"); @@ -1804,6 +1874,10 @@ mod test { .into(), }; + let locked_outpoints_changeset = locked_outpoints::ChangeSet { + outpoints: [(OutPoint::new(tx1.compute_txid(), 0), true)].into(), + }; + let mut changeset = ChangeSet { descriptor: Some(descriptor.clone()), change_descriptor: Some(change_descriptor.clone()), @@ -1811,6 +1885,7 @@ mod test { local_chain: local_chain_changeset, tx_graph: tx_graph_changeset, indexer: keychain_txout_changeset, + locked_outpoints: locked_outpoints_changeset, }; store.create_tables::().unwrap(); @@ -1855,6 +1930,10 @@ mod test { .into(), }; + let locked_outpoints_changeset = locked_outpoints::ChangeSet { + outpoints: [(OutPoint::new(tx2.compute_txid(), 0), true)].into(), + }; + let changeset_new = ChangeSet { descriptor: Some(descriptor), change_descriptor: Some(change_descriptor), @@ -1862,6 +1941,7 @@ mod test { local_chain: local_chain_changeset, tx_graph: tx_graph_changeset, indexer: keychain_txout_changeset, + locked_outpoints: locked_outpoints_changeset, }; store.persist_wallet(&changeset_new).unwrap(); From 10a5d04a0c19bac6fc75c91c720409af1a9d3818 Mon Sep 17 00:00:00 2001 From: Steve Myers Date: Sun, 3 May 2026 11:30:15 -0500 Subject: [PATCH 2/2] release: version to 0.2.0 --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index a8872e2..537a364 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bdk_redb" -version = "0.1.1" +version = "0.2.0" edition = "2024" license = "MIT OR Apache-2.0" description = "Persistence backend for bdk using redb"