Accept a custom database interface when starting the daemon
This commit is contained in:
parent
f365effacd
commit
ea3595349d
@ -40,7 +40,7 @@ fn update_tip(bit: &impl BitcoinInterface, db_conn: &mut Box<dyn DatabaseConnect
|
|||||||
/// `shutdown` atomic.
|
/// `shutdown` atomic.
|
||||||
pub fn looper(
|
pub fn looper(
|
||||||
bit: sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
|
bit: sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
|
||||||
db: impl DatabaseInterface,
|
db: sync::Arc<sync::Mutex<dyn DatabaseInterface>>,
|
||||||
shutdown: sync::Arc<atomic::AtomicBool>,
|
shutdown: sync::Arc<atomic::AtomicBool>,
|
||||||
poll_interval: time::Duration,
|
poll_interval: time::Duration,
|
||||||
) {
|
) {
|
||||||
|
|||||||
@ -19,7 +19,7 @@ pub struct Poller {
|
|||||||
impl Poller {
|
impl Poller {
|
||||||
pub fn start(
|
pub fn start(
|
||||||
bit: sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
|
bit: sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
|
||||||
db: impl DatabaseInterface + 'static,
|
db: sync::Arc<sync::Mutex<dyn DatabaseInterface>>,
|
||||||
poll_interval: time::Duration,
|
poll_interval: time::Duration,
|
||||||
) -> Poller {
|
) -> Poller {
|
||||||
let shutdown = sync::Arc::from(atomic::AtomicBool::from(false));
|
let shutdown = sync::Arc::from(atomic::AtomicBool::from(false));
|
||||||
|
|||||||
@ -2,7 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! External interface to the Minisafe daemon.
|
//! External interface to the Minisafe daemon.
|
||||||
|
|
||||||
use crate::{bitcoin::BitcoinInterface, DaemonControl, VERSION};
|
use crate::{bitcoin::BitcoinInterface, database::DatabaseInterface, DaemonControl, VERSION};
|
||||||
|
|
||||||
use miniscript::{
|
use miniscript::{
|
||||||
bitcoin,
|
bitcoin,
|
||||||
|
|||||||
@ -8,6 +8,8 @@ use crate::{
|
|||||||
database::sqlite::{schema::DbTip, SqliteConn, SqliteDb},
|
database::sqlite::{schema::DbTip, SqliteConn, SqliteDb},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use std::sync;
|
||||||
|
|
||||||
use miniscript::bitcoin::util::bip32;
|
use miniscript::bitcoin::util::bip32;
|
||||||
|
|
||||||
pub trait DatabaseInterface: Send {
|
pub trait DatabaseInterface: Send {
|
||||||
@ -20,6 +22,13 @@ impl DatabaseInterface for SqliteDb {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME: do we need to repeat the entire trait implemenation? Isn't there a nicer way?
|
||||||
|
impl DatabaseInterface for sync::Arc<sync::Mutex<dyn DatabaseInterface>> {
|
||||||
|
fn connection(&self) -> Box<dyn DatabaseConnection> {
|
||||||
|
self.lock().unwrap().connection()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait DatabaseConnection {
|
pub trait DatabaseConnection {
|
||||||
/// Get the tip of the best chain we've seen.
|
/// Get the tip of the best chain we've seen.
|
||||||
fn chain_tip(&mut self) -> Option<BlockChainTip>;
|
fn chain_tip(&mut self) -> Option<BlockChainTip>;
|
||||||
|
|||||||
59
src/lib.rs
59
src/lib.rs
@ -148,6 +148,31 @@ fn create_datadir(datadir_path: &path::Path) -> Result<(), StartupError> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Connect to the SQLite database. Create it if starting fresh, and do some sanity checks.
|
||||||
|
// If all went well, returns the interface to the SQLite database.
|
||||||
|
fn setup_sqlite(
|
||||||
|
config: &Config,
|
||||||
|
data_dir: &path::Path,
|
||||||
|
fresh_data_dir: bool,
|
||||||
|
) -> Result<SqliteDb, StartupError> {
|
||||||
|
let db_path: path::PathBuf = [data_dir, path::Path::new("minisafed.sqlite3")]
|
||||||
|
.iter()
|
||||||
|
.collect();
|
||||||
|
let options = if fresh_data_dir {
|
||||||
|
Some(FreshDbOptions {
|
||||||
|
bitcoind_network: config.bitcoind_config.network,
|
||||||
|
main_descriptor: config.main_descriptor.clone(),
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
let sqlite = SqliteDb::new(db_path, options)?;
|
||||||
|
sqlite.sanity_check(config.bitcoind_config.network, &config.main_descriptor)?;
|
||||||
|
log::info!("Database initialized and checked.");
|
||||||
|
|
||||||
|
Ok(sqlite)
|
||||||
|
}
|
||||||
|
|
||||||
// Connect to bitcoind. Setup the watchonly wallet, and do some sanity checks.
|
// Connect to bitcoind. Setup the watchonly wallet, and do some sanity checks.
|
||||||
// If all went well, returns the interface to bitcoind.
|
// If all went well, returns the interface to bitcoind.
|
||||||
fn setup_bitcoind(
|
fn setup_bitcoind(
|
||||||
@ -177,7 +202,8 @@ fn setup_bitcoind(
|
|||||||
pub struct DaemonControl {
|
pub struct DaemonControl {
|
||||||
config: Config,
|
config: Config,
|
||||||
bitcoin: sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
|
bitcoin: sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
|
||||||
db: Box<dyn DatabaseInterface>,
|
// FIXME: Should we require Sync on DatabaseInterface rather than using a Mutex?
|
||||||
|
db: sync::Arc<sync::Mutex<dyn DatabaseInterface>>,
|
||||||
secp: secp256k1::Secp256k1<secp256k1::VerifyOnly>,
|
secp: secp256k1::Secp256k1<secp256k1::VerifyOnly>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,7 +211,7 @@ impl DaemonControl {
|
|||||||
pub fn new(
|
pub fn new(
|
||||||
config: Config,
|
config: Config,
|
||||||
bitcoin: sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
|
bitcoin: sync::Arc<sync::Mutex<dyn BitcoinInterface>>,
|
||||||
db: Box<dyn DatabaseInterface>,
|
db: sync::Arc<sync::Mutex<dyn DatabaseInterface>>,
|
||||||
) -> DaemonControl {
|
) -> DaemonControl {
|
||||||
let secp = secp256k1::Secp256k1::verification_only();
|
let secp = secp256k1::Secp256k1::verification_only();
|
||||||
DaemonControl {
|
DaemonControl {
|
||||||
@ -207,12 +233,15 @@ impl DaemonHandle {
|
|||||||
///
|
///
|
||||||
/// You may specify a custom Bitcoin interface through the `bitcoin` parameter. If `None`, the
|
/// You may specify a custom Bitcoin interface through the `bitcoin` parameter. If `None`, the
|
||||||
/// default Bitcoin interface (`bitcoind` JSONRPC) will be used.
|
/// default Bitcoin interface (`bitcoind` JSONRPC) will be used.
|
||||||
|
/// You may specify a custom Database interface through the `db` parameter. If `None`, the
|
||||||
|
/// default Database interface (SQLite) will be used.
|
||||||
///
|
///
|
||||||
/// **Note**: we internally use threads, and set a panic hook. A downstream application must
|
/// **Note**: we internally use threads, and set a panic hook. A downstream application must
|
||||||
/// not overwrite this panic hook.
|
/// not overwrite this panic hook.
|
||||||
pub fn start(
|
pub fn start(
|
||||||
config: Config,
|
config: Config,
|
||||||
bitcoin: Option<impl BitcoinInterface + 'static>,
|
bitcoin: Option<impl BitcoinInterface + 'static>,
|
||||||
|
db: Option<impl DatabaseInterface + 'static>,
|
||||||
) -> Result<Self, StartupError> {
|
) -> Result<Self, StartupError> {
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
setup_panic_hook();
|
setup_panic_hook();
|
||||||
@ -229,20 +258,14 @@ impl DaemonHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Then set up the database
|
// Then set up the database
|
||||||
let db_path: path::PathBuf = [data_dir.as_path(), path::Path::new("minisafed.sqlite3")]
|
let db = match db {
|
||||||
.iter()
|
Some(db) => sync::Arc::from(sync::Mutex::from(db)),
|
||||||
.collect();
|
None => sync::Arc::from(sync::Mutex::from(setup_sqlite(
|
||||||
let options = if fresh_data_dir {
|
&config,
|
||||||
Some(FreshDbOptions {
|
&data_dir,
|
||||||
bitcoind_network: config.bitcoind_config.network,
|
fresh_data_dir,
|
||||||
main_descriptor: config.main_descriptor.clone(),
|
)?)) as sync::Arc<sync::Mutex<dyn DatabaseInterface>>,
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
};
|
||||||
let sqlite = SqliteDb::new(db_path, options)?;
|
|
||||||
sqlite.sanity_check(config.bitcoind_config.network, &config.main_descriptor)?;
|
|
||||||
log::info!("Database initialized and checked.");
|
|
||||||
|
|
||||||
// Now, set up the Bitcoin interface.
|
// Now, set up the Bitcoin interface.
|
||||||
let bit = match bitcoin {
|
let bit = match bitcoin {
|
||||||
@ -271,12 +294,12 @@ impl DaemonHandle {
|
|||||||
// Spawn the bitcoind poller with a retry limit high enough that we'd fail after that.
|
// Spawn the bitcoind poller with a retry limit high enough that we'd fail after that.
|
||||||
let bitcoin_poller = poller::Poller::start(
|
let bitcoin_poller = poller::Poller::start(
|
||||||
bit.clone(),
|
bit.clone(),
|
||||||
sqlite.clone(),
|
db.clone(),
|
||||||
config.bitcoind_config.poll_interval_secs,
|
config.bitcoind_config.poll_interval_secs,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Finally, set up the API.
|
// Finally, set up the API.
|
||||||
let control = DaemonControl::new(config, bit, Box::from(sqlite));
|
let control = DaemonControl::new(config, bit, db);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
control,
|
control,
|
||||||
@ -287,7 +310,7 @@ impl DaemonHandle {
|
|||||||
/// Start the Minisafe daemon with the default Bitcoin and database interfaces (`bitcoind` RPC
|
/// Start the Minisafe daemon with the default Bitcoin and database interfaces (`bitcoind` RPC
|
||||||
/// and SQLite).
|
/// and SQLite).
|
||||||
pub fn start_default(config: Config) -> Result<DaemonHandle, StartupError> {
|
pub fn start_default(config: Config) -> Result<DaemonHandle, StartupError> {
|
||||||
DaemonHandle::start(config, Option::<BitcoinD>::None)
|
DaemonHandle::start(config, Option::<BitcoinD>::None, Option::<SqliteDb>::None)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start the JSONRPC server and listen for incoming commands until we die.
|
/// Start the JSONRPC server and listen for incoming commands until we die.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user