-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ded34a7
commit 750eaaa
Showing
6 changed files
with
214 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
# Migrating from 0.X | ||
|
||
!!! tip | ||
This page is up-to-date with version `1.0.0-beta.5` of bdk. | ||
|
||
So you're ready to migrate to BDK version 1.0, congratulations! | ||
This document contains some helpful tips that, with the help of some automation, should make the process as seamless as possible. | ||
|
||
The below steps are for migrating wallet details from the old [`bdk` v0.30][0] to the new [`bdk_wallet` v1.0][1]. | ||
This procedure can be applied to wallets backed by a SQLite database. | ||
Of particular concern is the ability to restore the _last known address index_ for each keychain. | ||
This is important because without that metadata the new wallet may end up reusing receive addresses, which should be avoided for privacy reasons, although it should not cause loss of funds. | ||
|
||
!!! tip | ||
NB: The migration process outlined below will not automatically restore the wallet's transaction data or local view of the blockchain. | ||
Thanks to the public ledger however, we can restore all the pertinent information for this wallet using one of the blockchain client libraries supported by BDK. | ||
|
||
## Overview | ||
|
||
1. Load an old database | ||
1. Get last revealed addresses | ||
1. Create new wallet | ||
1. Restore revealed addresses | ||
1. Write to new database | ||
1. Rescan (recommended) | ||
|
||
<!-- overview --> | ||
```rust title="examples/rust/migrate-version/src/main.rs" | ||
--8<-- "examples/rust/migrate-version/src/main.rs:main" | ||
``` | ||
|
||
## Walkthrough | ||
|
||
In a new rust project add these dependencies to Cargo.toml | ||
|
||
```toml | ||
[dependencies] | ||
anyhow = "1" | ||
bdk = { version = "0.30", features = ["sqlite"] } | ||
bdk_wallet = { version = "=1.0.0-beta.5", features = ["rusqlite"] } | ||
``` | ||
|
||
Because there are two versions of bdk in the same project, we need to pay attention to how types are imported. | ||
To avoid name clashes or any sort of mismatch resolving types that appear similar, we use fully qualified syntax, for example `bdk::bitcoin::Network::Testnet`. | ||
You'll notice in some cases we can get around this annoyance by casting a value to another rust primitive or standard library type such as `String`. | ||
|
||
<!-- imports --> | ||
```rust title="examples/rust/migrate-version/src/main.rs" | ||
--8<-- "examples/rust/migrate-version/src/main.rs:use" | ||
``` | ||
|
||
<!-- setup --> | ||
Take a minute to define a few constants, for example the file path to the current database and the path to be used for the new database. | ||
The descriptors and network shown here are for illustration; you should substitute them with your own. | ||
Note that because we'll be creating a fresh database there should not already exist a persisted wallet at the new path. | ||
|
||
```rust title="examples/rust/migrate-version/src/main.rs" | ||
--8<-- "examples/rust/migrate-version/src/main.rs:setup" | ||
``` | ||
|
||
<!-- old --> | ||
Now retrieve the last revealed addresses from the `old_wallet`. | ||
|
||
```rust title="examples/rust/migrate-version/src/main.rs" | ||
--8<-- "examples/rust/migrate-version/src/main.rs:old" | ||
``` | ||
|
||
<!-- new --> | ||
For the `new_wallet` we should be using the same descriptors and network as before. | ||
If the given descriptors contain secret keys, then the wallet will be able to sign transactions as well. | ||
|
||
```rust title="examples/rust/migrate-version/src/main.rs" | ||
--8<-- "examples/rust/migrate-version/src/main.rs:new" | ||
``` | ||
|
||
<!-- sync --> | ||
Now that we have a new database and have properly restored our addresses, you'll likely want to rescan the blockchain to recover the wallet's transactions. | ||
Below is an example of doing a `full_scan` using `bdk_esplora` but the exact method of syncing will depend on your application. | ||
Happy migrating and see you on [v1.0][1]! | ||
|
||
```rust title="examples/rust/migrate-version/src/main.rs" | ||
--8<-- "examples/rust/migrate-version/src/main.rs:sync" | ||
``` | ||
|
||
[0]: https://docs.rs/bdk/0.30.0/bdk/ | ||
[1]: https://docs.rs/bdk_wallet/1.0.0-beta.5/bdk_wallet/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "migrate-version" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
anyhow = "1" | ||
bdk = { version = "0.30", features = ["sqlite"] } | ||
bdk_wallet = { version = "=1.0.0-beta.5", features = ["rusqlite"] } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
// --8<-- [start:use] | ||
use anyhow::Context; | ||
|
||
use bdk::database::SqliteDatabase; | ||
use bdk::wallet::AddressIndex; | ||
|
||
use bdk_wallet::bitcoin::Network; | ||
use bdk_wallet::rusqlite; | ||
use bdk_wallet::KeychainKind; | ||
use bdk_wallet::Wallet; | ||
// --8<-- [end:use] | ||
|
||
// --8<-- [start:setup] | ||
const EXTERNAL_DESCRIPTOR: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)"; | ||
const INTERNAL_DESCRIPTOR: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/1/*)"; | ||
const NETWORK: Network = Network::Testnet; | ||
|
||
// path to old db | ||
const BDK_DB_PATH: &str = "./.bdk-example.sqlite"; | ||
// path to new db | ||
const BDK_WALLET_DB_PATH: &str = "./.bdk-wallet-example.sqlite"; | ||
// --8<-- [end:setup] | ||
|
||
// Steps for migrating wallet state from the old `bdk` 0.30 to the new `bdk_wallet` 1.0. | ||
|
||
// To run: change `BDK_DB_PATH` to point to the location of the old database file and | ||
// modify the descriptors and network above to fit your setup. Before running, there | ||
// should not be any persisted data at the new path `BDK_WALLET_DB_PATH`. | ||
|
||
// --8<-- [start:main] | ||
fn main() -> anyhow::Result<()> { | ||
// --8<-- [start:old] | ||
// Open old wallet | ||
let db = SqliteDatabase::new(BDK_DB_PATH); | ||
let old_wallet = bdk::Wallet::new( | ||
EXTERNAL_DESCRIPTOR, | ||
Some(INTERNAL_DESCRIPTOR), | ||
bdk::bitcoin::Network::Testnet, | ||
db, | ||
)?; | ||
|
||
// Get last revealed addresses for each keychain | ||
let addr = old_wallet.get_address(AddressIndex::LastUnused)?; | ||
println!("Last revealed external {} {}", addr.index, addr.address); | ||
let external_derivation_index = addr.index; | ||
let last_revealed_external = addr.address.to_string(); | ||
|
||
let addr = old_wallet.get_internal_address(AddressIndex::LastUnused)?; | ||
println!("Last revealed internal {} {}", addr.index, addr.address); | ||
let internal_derivation_index = addr.index; | ||
let last_revealed_internal = addr.address.to_string(); | ||
// --8<-- [end:old] | ||
|
||
// --8<-- [start:new] | ||
// Create new wallet | ||
let mut db = rusqlite::Connection::open(BDK_WALLET_DB_PATH)?; | ||
let mut new_wallet = Wallet::create(EXTERNAL_DESCRIPTOR, INTERNAL_DESCRIPTOR) | ||
.network(NETWORK) | ||
.create_wallet(&mut db) | ||
.context("failed to create wallet")?; | ||
|
||
// Retore revealed addresses | ||
let _ = new_wallet.reveal_addresses_to(KeychainKind::External, external_derivation_index); | ||
let _ = new_wallet.reveal_addresses_to(KeychainKind::Internal, internal_derivation_index); | ||
|
||
// Persist new wallet | ||
new_wallet.persist(&mut db)?; | ||
|
||
println!("\n========== New database created. =========="); | ||
|
||
let addr = new_wallet | ||
.list_unused_addresses(KeychainKind::External) | ||
.last() | ||
.unwrap(); | ||
assert_eq!(addr.to_string(), last_revealed_external); | ||
println!("Last revealed external {} {}", addr.index, addr.address); | ||
let addr = new_wallet | ||
.list_unused_addresses(KeychainKind::Internal) | ||
.last() | ||
.unwrap(); | ||
println!("Last revealed internal {} {}", addr.index, addr.address); | ||
assert_eq!(addr.to_string(), last_revealed_internal); | ||
// --8<-- [end:new] | ||
|
||
Ok(()) | ||
} | ||
// --8<-- [end:main] | ||
|
||
/* Extra: sync with esplora | ||
// --8<-- [start:sync] | ||
use bdk_esplora::{esplora_client, EsploraExt}; | ||
let client = esplora_client::Builder::new(ESPLORA_URL).build_blocking(); | ||
let request = wallet.start_full_scan().inspect({ | ||
let mut stdout = std::io::stdout(); | ||
let mut once = BTreeSet::<KeychainKind>::new(); | ||
move |keychain, spk_i, _| { | ||
if once.insert(keychain) { | ||
print!("\nScanning keychain [{:?}] ", keychain); | ||
} | ||
print!(" {:<3}", spk_i); | ||
stdout.flush().unwrap(); | ||
} | ||
}); | ||
let update = client.full_scan(request, STOP_GAP, PARALLEL_REQUESTS)?; | ||
wallet.apply_update(update)?; | ||
wallet.persist(&mut db)?; | ||
// --8<-- [end:sync] | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters