From 8756a9fb89e4a8a4f1c8cc65de3c394b96230063 Mon Sep 17 00:00:00 2001 From: valued mammal Date: Thu, 17 Oct 2024 12:39:47 -0400 Subject: [PATCH] wip: update example to use sqlite Requires a patch to bdk release/0.29 to update rusqlite dependency to 0.31 --- .../example_migrate_wallet/Cargo.toml | 5 +- .../example_migrate_wallet/src/main.rs | 82 +++++++++---------- 2 files changed, 42 insertions(+), 45 deletions(-) diff --git a/example-crates/example_migrate_wallet/Cargo.toml b/example-crates/example_migrate_wallet/Cargo.toml index 5e3486118..814d992bb 100644 --- a/example-crates/example_migrate_wallet/Cargo.toml +++ b/example-crates/example_migrate_wallet/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" [dependencies] -bdk = { version = "0.29" } +anyhow = "1" +# TODO: We should be able to patch bdk v0.29 to depend on rusqlite v0.31 +#bdk = { version = "0.30", features = ["sqlite"] } +bdk = { git = "https://github.com/ValuedMammal/bdk", branch = "release/0.29-deps-rusqlite", features = ["sqlite"] } bdk_wallet = { path = "../../crates/wallet", features = ["rusqlite"] } bdk_electrum = { path = "../../crates/electrum" } diff --git a/example-crates/example_migrate_wallet/src/main.rs b/example-crates/example_migrate_wallet/src/main.rs index dce75439e..68edf9173 100644 --- a/example-crates/example_migrate_wallet/src/main.rs +++ b/example-crates/example_migrate_wallet/src/main.rs @@ -2,42 +2,37 @@ use std::collections::HashSet; use std::io::Write; use bdk::bitcoin::Network; -use bdk::sled; +use bdk::database::SqliteDatabase; use bdk::wallet::{AddressIndex, Wallet}; use bdk_electrum::{electrum_client, BdkElectrumClient}; use bdk_wallet::rusqlite; -const DESC: &str = "tr([83737d5e/86'/1'/0']tpubDDR5GgtoxS8fJyjjvdahN4VzV5DV6jtbcyvVXhEKq2XtpxjxBXmxH3r8QrNbQqHg4bJM1EGkxi7Pjfkgnui9jQWqS7kxHvX6rhUeriLDKxz/0/*)"; -const CHANGE_DESC: &str = "tr([83737d5e/86'/1'/0']tpubDDR5GgtoxS8fJyjjvdahN4VzV5DV6jtbcyvVXhEKq2XtpxjxBXmxH3r8QrNbQqHg4bJM1EGkxi7Pjfkgnui9jQWqS7kxHvX6rhUeriLDKxz/1/*)"; +const DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; +const CHANGE_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; const ELECTRUM_URL: &str = "ssl://mempool.space:60602"; const NETWORK: Network = Network::Signet; -// Steps for migrating wallet details from bdk v0.29 to bdk_wallet v1.0. For this example we assume -// the previous wallet was backed by `sled` and the new wallet will use sqlite. (steps will be -// similar if coming from sqlite, but we can't easily depend on multiple versions of rusqlite -// in the same project). +const BDK_DB_PATH: &str = "example-bdk.sqlite"; +const BDK_WALLET_DB_PATH: &str = "example-bdk-wallet.sqlite"; -// Run this with `cargo run` and optionally providing a path and tree name -// for an existing sled db +// Steps for migrating wallet details from bdk v0.29 to bdk_wallet v1.0. These steps +// are applicable to wallets backed by a SQLite database. -// Usage: `cargo run -- [db_path tree_name]` +// May be run with no arguments or optionally providing a path to an existing sqlite db +// e.g. `cargo run --bin example_migrate_wallet -- ` -fn main() -> Result<(), Box> { +fn main() -> anyhow::Result<()> { let cargo_dir = std::env::var("CARGO_MANIFEST_DIR")?; // Accept a db config passed in via command line, or else create a new one for testing - let mut args = std::env::args(); - _ = args.next(); - let args: Vec = args.collect(); - + let args: Vec = std::env::args().collect(); let db = if args.len() < 2 { - let path = format!("{}/sled", cargo_dir); - sled::open(path)?.open_tree("wallet")? + let path = format!("{}/{}", cargo_dir, BDK_DB_PATH); + SqliteDatabase::new(path) } else { - let path = args[0].to_string(); - let tree_name = args[1].to_string(); - sled::open(path)?.open_tree(tree_name)? + let path = args[1].to_string(); + SqliteDatabase::new(path) }; // Open wallet @@ -46,39 +41,36 @@ fn main() -> Result<(), Box> { // Get last revealed addresses for each keychain let addr = wallet.get_address(AddressIndex::LastUnused)?; println!("Last revealed external {} {}", addr.index, addr.address); - let last_revealed_external = addr.index; + let external_derivation_index = addr.index; + let last_revealed_external = addr.address.to_string(); let addr = wallet.get_internal_address(AddressIndex::LastUnused)?; println!("Last revealed internal {} {}", addr.index, addr.address); - let last_revealed_internal = addr.index; - - // Get descriptors - // Or we can use the same `DESC` and `CHANGE_DESC` from above. - // let descriptor = wallet.public_descriptor(KeychainKind::External)?.unwrap().to_string(); - // let change_descriptor = wallet.public_descriptor(KeychainKind::Internal)?.unwrap().to_string(); - - // Note: - // If wallet 1 was created with signing keys we could try to get the signers from it as well, however - // we'll get a compiler error if we try to pass these to wallet 2, as it is using a different version - // of rust-miniscript. Since the old bdk made you provide the descriptors at startup, we can assume - // they are also available when doing the migration, so private keys will carry over if desired. - // let signers_external = wallet.get_signers(KeychainKind::External).as_key_map(wallet.secp_ctx()); - // let signers_internal = wallet.get_signers(KeychainKind::Internal).as_key_map(wallet.secp_ctx()); - drop(wallet); + let internal_derivation_index = addr.index; + let last_revealed_internal = addr.address.to_string(); // Create new wallet - let new_db_path = format!("{}/wallet.sqlite", cargo_dir); + // For the new bdk wallet we pass in the same descriptors as before. If the given descriptors + // contain secret keys the wallet will be able to sign transactions as well. + let new_db_path = format!("{}/{}", cargo_dir, BDK_WALLET_DB_PATH); let mut db = rusqlite::Connection::open(new_db_path)?; - let mut wallet = match bdk_wallet::Wallet::load().load_wallet(&mut db)? { - Some(wallet) => wallet, - None => bdk_wallet::Wallet::create(DESC, CHANGE_DESC) - .network(bdk_wallet::bitcoin::Network::Signet) - .create_wallet(&mut db)?, + let mut wallet = match bdk_wallet::Wallet::create(DESC, CHANGE_DESC) + .network(bdk_wallet::bitcoin::Network::Signet) + .create_wallet(&mut db) + { + Ok(wallet) => wallet, + Err(_) => anyhow::bail!("should not have existing db"), }; // Retore revealed addresses - let _ = wallet.reveal_addresses_to(bdk_wallet::KeychainKind::External, last_revealed_external); - let _ = wallet.reveal_addresses_to(bdk_wallet::KeychainKind::Internal, last_revealed_internal); + let _ = wallet.reveal_addresses_to( + bdk_wallet::KeychainKind::External, + external_derivation_index, + ); + let _ = wallet.reveal_addresses_to( + bdk_wallet::KeychainKind::Internal, + internal_derivation_index, + ); // Remember to persist the new wallet wallet.persist(&mut db)?; @@ -89,12 +81,14 @@ fn main() -> Result<(), Box> { .list_unused_addresses(bdk_wallet::KeychainKind::External) .last() .unwrap(); + assert_eq!(addr.to_string(), last_revealed_external); println!("Last revealed external {} {}", addr.index, addr.address); let addr = wallet .list_unused_addresses(bdk_wallet::KeychainKind::Internal) .last() .unwrap(); println!("Last revealed internal {} {}", addr.index, addr.address); + assert_eq!(addr.to_string(), last_revealed_internal); // Now that we migrated the wallet details, you likely want to rescan the blockchain // to restore transaction data for wallet 2. Here we're using the bdk_electrum client