Skip to content

Commit

Permalink
feat: add bdk_sqlite_store crate implementing PersistBackend backed b…
Browse files Browse the repository at this point in the history
…y a SQLite database
  • Loading branch information
notmandatory committed Apr 11, 2024
1 parent 2045e9c commit f9447a1
Show file tree
Hide file tree
Showing 7 changed files with 692 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ Cargo.lock

# Example persisted files.
*.db
*.sqlite
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"crates/bdk",
"crates/chain",
"crates/file_store",
"crates/sqlite_store",
"crates/electrum",
"crates/esplora",
"crates/bitcoind_rpc",
Expand Down
22 changes: 22 additions & 0 deletions crates/sqlite_store/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[package]
name = "bdk_sqlite_store"
version = "0.1.0"
edition = "2021"
license = "MIT OR Apache-2.0"
repository = "https://github.com/bitcoindevkit/bdk"
documentation = "https://docs.rs/bdk_file_store"
description = "A simple append-only SQLite based implementation of Persist for Bitcoin Dev Kit."
keywords = ["bitcoin", "persist", "persistence", "bdk", "sqlite"]
authors = ["Bitcoin Dev Kit Developers"]
readme = "README.md"

[dependencies]
bdk_chain = { path = "../chain", version = "0.10.0", features = [ "serde", "miniscript" ] }
rusqlite = { version = "0.31.0", features = ["bundled"]}
serde = { version = "1", features = ["derive"] }
serde_json = "1"
log = "0.4.21"
hex = "0.4.3"

[dev-dependencies]
tempfile = "3"
11 changes: 11 additions & 0 deletions crates/sqlite_store/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# BDK SQLite Store

This is a simple append-only [SQLite] database backed implementation of
[`Persist`](`bdk_chain::Persist`).

The main structure is [`Store`](`crate::Store`), which can be used with [`bdk`]'s
`Wallet` to persist wallet data into a SQLite database file.

[`bdk`]: https://docs.rs/bdk/latest
[`bdk_chain`]: https://docs.rs/bdk_chain/latest
[SQLite]: https://www.sqlite.org/index.html
81 changes: 81 additions & 0 deletions crates/sqlite_store/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#![doc = include_str!("../README.md")]

mod store;

use bdk_chain::{indexed_tx_graph, keychain, local_chain, Append, ConfirmationHeightAnchor};
use serde::{Deserialize, Serialize};
pub use store::*;

#[derive(Clone, Debug, PartialEq)]
struct ChangeSet<K: Ord + for<'de> Deserialize<'de> + Serialize>(
local_chain::ChangeSet,
indexed_tx_graph::ChangeSet<ConfirmationHeightAnchor, keychain::ChangeSet<K>>,
);

impl<K> Append for ChangeSet<K>
where
K: Ord + for<'de> Deserialize<'de> + Serialize,
{
fn append(&mut self, mut other: Self) {
self.0.append(&mut other.0);
self.1.append(other.1);
}

fn is_empty(&self) -> bool {
self.0.is_empty() && self.1.is_empty()
}
}

/// Error that occurs while writing new change sets to the DB.
#[derive(Debug)]
pub enum WriteError {
/// JSON encoding error.
Json(serde_json::Error),
/// SQLite error.
Sqlite(rusqlite::Error),
}

impl core::fmt::Display for WriteError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Json(e) => write!(f, "json error trying to write change set: {}", e),
Self::Sqlite(e) => write!(f, "sqlite error trying to write change set: {}", e),
}
}
}

impl From<serde_json::Error> for WriteError {
fn from(error: serde_json::Error) -> Self {
Self::Json(error)
}
}

impl std::error::Error for WriteError {}

/// Error the occurs while reading stored change sets from the DB.
#[derive(Debug)]
pub enum ReadError {
/// Json decoding
Json {
rowid: usize,
err: serde_json::Error,
},
/// Sqlite error
Sqlite(rusqlite::Error),
/// FromSql error
FromSql(rusqlite::types::FromSqlError),
}

impl core::fmt::Display for ReadError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
ReadError::Json { rowid, err } => write!(
f,
"json error trying to decode change set {}, {}",
rowid, err
),
ReadError::Sqlite(err) => write!(f, "Sqlite error {}", err),
ReadError::FromSql(err) => write!(f, "FromSql error {}", err),
}
}
}
Loading

0 comments on commit f9447a1

Please sign in to comment.