From 24254bbd574fb98234a82fedab81c27636d8fc22 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Tue, 17 Mar 2026 22:06:30 -0400 Subject: [PATCH 1/2] schema: Add migration `0003_schema.up.sql` `0003_schema.up.sql` adds a table for locked outpoints. This will become relevant when we update `bdk_wallet` to 3.0. --- migrations/0003_schema.up.sql | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 migrations/0003_schema.up.sql diff --git a/migrations/0003_schema.up.sql b/migrations/0003_schema.up.sql new file mode 100644 index 0000000..3e57469 --- /dev/null +++ b/migrations/0003_schema.up.sql @@ -0,0 +1,7 @@ +-- Add locked outpoints table + +CREATE TABLE IF NOT EXISTS locked_outpoint( + txid TEXT NOT NULL, + vout INTEGER NOT NULL, + PRIMARY KEY(txid, vout) +); From 24ba6827e9c6f1182d58293f8cb1d391b5054c4c Mon Sep 17 00:00:00 2001 From: valued mammal Date: Tue, 17 Mar 2026 22:08:16 -0400 Subject: [PATCH 2/2] wip,deps: Update `bdk_wallet` to 3.0.0-rc.1 feat: Update wallet code to read/write locked outpoints TODO: - Update bdk_wallet dependency to 3.0.0 --- Cargo.toml | 2 +- src/wallet.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d522c76..ccc4deb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" [dependencies] bdk_chain = { version = "0.23.2", features = ["miniscript"] } -bdk_wallet = { version = "2.3.0", optional = true } +bdk_wallet = { version = "3.0.0-rc.1", optional = true } sqlx = { version = "0.8.6", features = ["sqlite", "runtime-tokio"] } [dev-dependencies] diff --git a/src/wallet.rs b/src/wallet.rs index a34b588..ab45d95 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -4,8 +4,9 @@ use std::{collections::BTreeMap, pin::Pin, str::FromStr}; use bdk_chain::bitcoin; use bdk_chain::miniscript; -use bdk_wallet::{AsyncWalletPersister, ChangeSet, KeychainKind}; +use bdk_wallet::{AsyncWalletPersister, ChangeSet, KeychainKind, locked_outpoints}; use bitcoin::Network; +use bitcoin::OutPoint; use miniscript::descriptor::{Descriptor, DescriptorPublicKey}; use sqlx::Row; @@ -31,6 +32,8 @@ impl Store { self.write_local_chain(&changeset.local_chain).await?; self.write_tx_graph(&changeset.tx_graph).await?; self.write_keychain_txout(&changeset.indexer).await?; + self.write_locked_outpoints(&changeset.locked_outpoints) + .await?; Ok(()) } @@ -76,6 +79,7 @@ impl Store { let tx_graph = self.read_tx_graph().await?; let local_chain = self.read_local_chain().await?; let indexer = self.read_keychain_txout().await?; + let locked_outpoints = self.read_locked_outpoints().await?; Ok(ChangeSet { network, @@ -84,6 +88,7 @@ impl Store { tx_graph, local_chain, indexer, + locked_outpoints, }) } @@ -126,6 +131,49 @@ impl Store { Ok(descriptors) } + + /// Write locked outpoints. + pub async fn write_locked_outpoints( + &self, + locked_outpoints: &locked_outpoints::ChangeSet, + ) -> Result<(), Error> { + for (&outpoint, &is_locked) in &locked_outpoints.outpoints { + let OutPoint { txid, vout } = outpoint; + if is_locked { + sqlx::query("INSERT OR IGNORE INTO locked_outpoint(txid, vout) VALUES($1, $2)") + .bind(txid.to_string()) + .bind(vout) + .execute(&self.pool) + .await?; + } else { + sqlx::query("DELETE FROM locked_outpoint WHERE txid = $1 AND vout = $2") + .bind(txid.to_string()) + .bind(vout) + .execute(&self.pool) + .await?; + } + } + + Ok(()) + } + + /// Read locked outpoints. + pub async fn read_locked_outpoints(&self) -> Result { + let mut changeset = locked_outpoints::ChangeSet::default(); + + let rows = sqlx::query("SELECT txid, vout FROM locked_outpoint") + .fetch_all(&self.pool) + .await?; + for row in rows { + let txid: String = row.get("txid"); + let txid: bitcoin::Txid = txid.parse()?; + let vout: u32 = row.get("vout"); + let outpoint = OutPoint { txid, vout }; + changeset.outpoints.insert(outpoint, true); + } + + Ok(changeset) + } } type FutureResult<'a, T, E> = Pin> + 'a + Send>>;