Skip to content
This repository has been archived by the owner on Sep 12, 2023. It is now read-only.

Commit

Permalink
Merge #2174
Browse files Browse the repository at this point in the history
2174: Use file-based db for wallet r=bonomat a=bonomat

This will improve performance because
- we don't have to generate the addresses on every restart, we can just read them from the db
- we don't have to generate 1000 addresses after are restart to miss unspent addresses: if the wallet db grows naturally, i.e. all addresses are cached, we don't have to pre-cache addresses
- we use less memory as we don't have to store the whole db in memory: particularly when having 1000s spent addresses this is a significant amount of data
- sync is faster because we don't have re-fetch the whole raw tx which is stored in the db

Note: if you have used the daemon for some time in the past the first restart can be scary because it will not immediately fetch all addresses. It will take some time until the wallet has caught up because we only sync for 100 addresses at a time.

Fixes #2158 

Note 2: this depends on a bdk fork "https://github.com/coblox/bdk", branch = "sqlite-version-bumps"  because of incompatible dependency versions. I'll see to contribute this upstream


Co-authored-by: Philipp Hoenisch <[email protected]>
  • Loading branch information
bors[bot] and bonomat authored Jun 15, 2022
2 parents dd006c6 + 5a2b016 commit 4abf47d
Show file tree
Hide file tree
Showing 10 changed files with 73 additions and 29 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- Migrate away from JSON blobs in the DB to a more normalized database for RolloverCompleted events
- Use sled database for wallet. The wallet file is stored in your data-dir as either `maker-wallet` for the maker and `taker-wallet` for the taker respectively

## [0.4.20] - 2022-05-26

Expand Down
16 changes: 13 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion bdk-ext/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ edition = "2021"

[dependencies]
anyhow = "1"
bdk = { version = "0.18", default-features = false }
bdk = { version = "0.18", default-features = false, features = ["key-value-db"] }
rand = "0.6"
secp256k1 = { version = "0.20", features = ["rand", "global-context-less-secure"] }
tempfile = "3.3.0"
2 changes: 1 addition & 1 deletion daemon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ anyhow = "1"
async-stream = "0.3"
async-trait = "0.1.56"
asynchronous-codec = { version = "0.6.0", features = ["json"] }
bdk = { version = "0.18", default-features = false, features = ["electrum"] }
bdk = { version = "0.18", default-features = false, features = ["key-value-db"] }
bdk-ext = { path = "../bdk-ext" }
btsieve = { path = "../btsieve" }
bytes = "1"
Expand Down
50 changes: 31 additions & 19 deletions daemon/src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use bdk::bitcoin::Txid;
use bdk::blockchain::Blockchain;
use bdk::blockchain::ElectrumBlockchain;
use bdk::database::BatchDatabase;
use bdk::sled;
use bdk::wallet::tx_builder::TxOrdering;
use bdk::wallet::AddressIndex;
use bdk::FeeRate;
Expand All @@ -25,6 +26,7 @@ use model::TxFeeRate;
use model::WalletInfo;
use statrs::statistics::*;
use std::collections::HashSet;
use std::path::PathBuf;
use std::time::Duration;
use std::time::Instant;
use tokio::sync::watch;
Expand All @@ -33,6 +35,8 @@ use xtra_productivity::xtra_productivity;
use xtras::SendInterval;

const SYNC_INTERVAL: Duration = Duration::from_secs(3 * 60);
pub const MAKER_WALLET_ID: &str = "maker-wallet";
pub const TAKER_WALLET_ID: &str = "taker-wallet";

static BALANCE_GAUGE: conquer_once::Lazy<prometheus::Gauge> = conquer_once::Lazy::new(|| {
prometheus::register_gauge!(
Expand Down Expand Up @@ -83,8 +87,8 @@ static STD_DEV_UTXO_VALUE_GAUGE: conquer_once::Lazy<prometheus::Gauge> =
.unwrap()
});

pub struct Actor<B> {
wallet: bdk::Wallet<bdk::database::MemoryDatabase>,
pub struct Actor<B, DB> {
wallet: bdk::Wallet<DB>,
blockchain_client: B,
used_utxos: LockedUtxos,
tasks: Tasks,
Expand All @@ -95,15 +99,19 @@ pub struct Actor<B> {
#[error("The transaction is already in the blockchain")]
pub struct TransactionAlreadyInBlockchain;

impl Actor<ElectrumBlockchain> {
impl Actor<ElectrumBlockchain, sled::Tree> {
pub fn new(
electrum_rpc_url: &str,
ext_priv_key: ExtendedPrivKey,
db_path: PathBuf,
wallet_name: String,
) -> Result<(Self, watch::Receiver<Option<WalletInfo>>)> {
let client = bdk::electrum_client::Client::new(electrum_rpc_url)
.context("Failed to initialize Electrum RPC client")?;

let db = bdk::database::MemoryDatabase::new();
// Create a database (using default sled type) to store wallet data
let db = sled::open(db_path)?;
let db = db.open_tree(wallet_name)?;

let wallet = bdk::Wallet::new(
bdk::template::Bip84(ext_priv_key, KeychainKind::External),
Expand Down Expand Up @@ -133,7 +141,10 @@ impl Actor<ElectrumBlockchain> {
}
}

impl Actor<ElectrumBlockchain> {
impl<DB> Actor<ElectrumBlockchain, DB>
where
DB: BatchDatabase,
{
fn sync_internal(&mut self) -> Result<WalletInfo> {
let now = Instant::now();
tracing::trace!(target : "wallet", "Wallet sync started");
Expand Down Expand Up @@ -174,7 +185,10 @@ impl Actor<ElectrumBlockchain> {
}

#[xtra_productivity]
impl Actor<ElectrumBlockchain> {
impl<DB> Actor<ElectrumBlockchain, DB>
where
DB: BatchDatabase,
{
pub fn handle_sync(&mut self, _msg: Sync) {
let wallet_info_update = match self.sync_internal() {
Ok(wallet_info) => Some(wallet_info),
Expand Down Expand Up @@ -239,9 +253,10 @@ impl Actor<ElectrumBlockchain> {
}

#[xtra_productivity]
impl<B> Actor<B>
impl<B, DB> Actor<B, DB>
where
Self: xtra::Actor,
DB: BatchDatabase,
{
pub fn handle_sign(&mut self, msg: Sign) -> Result<PartiallySignedTransaction> {
let mut psbt = msg.psbt;
Expand Down Expand Up @@ -281,20 +296,14 @@ where
}

#[async_trait]
impl xtra::Actor for Actor<ElectrumBlockchain> {
impl<DB: 'static> xtra::Actor for Actor<ElectrumBlockchain, DB>
where
DB: BatchDatabase + Send,
{
type Stop = ();
async fn started(&mut self, ctx: &mut xtra::Context<Self>) {
let this = ctx.address().expect("self to be alive");

// We only cache the addresses at startup
if let Err(e) = self
.wallet
.ensure_addresses_cached(1000)
.with_context(|| "Could not cache addresses")
{
tracing::warn!("{:#}", e);
}

self.tasks.add(this.send_interval(SYNC_INTERVAL, || Sync));
}

Expand Down Expand Up @@ -438,7 +447,7 @@ mod tests {
use std::collections::HashSet;
use xtra::Actor as _;

impl Actor<()> {
impl Actor<(), bdk::database::MemoryDatabase> {
pub fn new_offline(
utxo_amount: Amount,
num_utxos: u8,
Expand All @@ -462,7 +471,10 @@ mod tests {
}

#[async_trait]
impl xtra::Actor for Actor<()> {
impl<DB: 'static> xtra::Actor for Actor<(), DB>
where
DB: Send,
{
type Stop = ();

async fn stopped(self) -> Self::Stop {}
Expand Down
11 changes: 10 additions & 1 deletion maker/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use daemon::projection;
use daemon::seed::RandomSeed;
use daemon::seed::Seed;
use daemon::wallet;
use daemon::wallet::MAKER_WALLET_ID;
use daemon::HEARTBEAT_INTERVAL;
use daemon::N_PAYOUTS;
use maker::routes;
Expand Down Expand Up @@ -65,7 +66,15 @@ async fn main() -> Result<()> {

let mut tasks = Tasks::default();

let (wallet, wallet_feed_receiver) = wallet::Actor::new(opts.network.electrum(), ext_priv_key)?;
let mut wallet_dir = data_dir.clone();

wallet_dir.push(MAKER_WALLET_ID);
let (wallet, wallet_feed_receiver) = wallet::Actor::new(
opts.network.electrum(),
ext_priv_key,
wallet_dir,
MAKER_WALLET_ID.to_string(),
)?;

let wallet = wallet.create(None).spawn(&mut tasks);

Expand Down
3 changes: 2 additions & 1 deletion maker/src/routes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::actor_system::ActorSystem;
use anyhow::Result;
use bdk::sled;
use daemon::bdk::blockchain::ElectrumBlockchain;
use daemon::oracle;
use daemon::projection::Cfd;
Expand Down Expand Up @@ -35,7 +36,7 @@ use tokio::select;
use tokio::sync::watch;
use uuid::Uuid;

pub type Maker = ActorSystem<oracle::Actor, wallet::Actor<ElectrumBlockchain>>;
pub type Maker = ActorSystem<oracle::Actor, wallet::Actor<ElectrumBlockchain, sled::Tree>>;

#[allow(clippy::too_many_arguments)]
#[rocket::get("/feed")]
Expand Down
3 changes: 2 additions & 1 deletion shared-bin/src/logger.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ pub fn init(level: LevelFilter, json_format: bool) -> Result<()> {
.add_directive("bdk::wallet=off".parse()?) // bdk logs derivation of addresses on INFO
.add_directive("_=off".parse()?) // rocket logs headers on INFO and uses `_` as the log target for it?
.add_directive("rocket=off".parse()?) // disable rocket logs: we have our own
.add_directive("xtra=warn".parse()?);
.add_directive("xtra=warn".parse()?)
.add_directive("sled=warn".parse()?); // downgrade sled log level: it is spamming too much on DEBUG

let builder = tracing_subscriber::fmt()
.with_env_filter(filter)
Expand Down
10 changes: 9 additions & 1 deletion taker/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ use daemon::seed::RandomSeed;
use daemon::seed::Seed;
use daemon::seed::UmbrelSeed;
use daemon::wallet;
use daemon::wallet::TAKER_WALLET_ID;
use daemon::Environment;
use daemon::TakerActorSystem;
use daemon::HEARTBEAT_INTERVAL;
Expand Down Expand Up @@ -299,7 +300,14 @@ async fn main() -> Result<()> {

let mut tasks = Tasks::default();

let (wallet, wallet_feed_receiver) = wallet::Actor::new(network.electrum(), ext_priv_key)?;
let mut wallet_dir = data_dir.clone();
wallet_dir.push(TAKER_WALLET_ID);
let (wallet, wallet_feed_receiver) = wallet::Actor::new(
network.electrum(),
ext_priv_key,
wallet_dir,
TAKER_WALLET_ID.to_string(),
)?;

let wallet = wallet.create(None).spawn(&mut tasks);

Expand Down
3 changes: 2 additions & 1 deletion taker/src/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use daemon::bdk;
use daemon::bdk::bitcoin::Amount;
use daemon::bdk::bitcoin::Network;
use daemon::bdk::blockchain::ElectrumBlockchain;
use daemon::bdk::sled;
use daemon::connection::ConnectionStatus;
use daemon::oracle;
use daemon::projection;
Expand Down Expand Up @@ -38,7 +39,7 @@ use uuid::Uuid;

type Taker = TakerActorSystem<
oracle::Actor,
wallet::Actor<ElectrumBlockchain>,
wallet::Actor<ElectrumBlockchain, sled::Tree>,
xtra_bitmex_price_feed::Actor,
>;

Expand Down

0 comments on commit 4abf47d

Please sign in to comment.