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/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) +); 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>>;