Skip to content

Explorations for support of N keychains #226

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@
resolver = "2"
members = [
"wallet",
"examples/example_wallet_electrum",
"examples/example_wallet_esplora_blocking",
"examples/example_wallet_esplora_async",
"examples/example_wallet_rpc",
"examples/example_n_keychains",
# "examples/example_wallet_electrum",
# "examples/example_wallet_esplora_blocking",
# "examples/example_wallet_esplora_async",
# "examples/example_wallet_rpc",
]

[workspace.package]
Expand Down
12 changes: 12 additions & 0 deletions examples/example_n_keychains/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[package]
name = "example_wallet_esplora_blocking"
version = "0.2.0"
edition = "2021"
publish = false

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
bdk_wallet = { path = "../../wallet" }
bdk_esplora = { version = "0.20", features = ["blocking"] }
anyhow = "1"
53 changes: 53 additions & 0 deletions examples/example_n_keychains/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
use bdk_esplora::{esplora_client, EsploraExt};
use bdk_wallet::chain::DescriptorId;
use bdk_wallet::Wallet;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::KeychainKind;
use bdk_wallet::keyring::KeyRing;

const ESPLORA_URL: &str = "http://signet.bitcoindevkit.net";
const STOP_GAP: usize = 5;
const PARALLEL_REQUESTS: usize = 5;

const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
const OTHER_DESC_21: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/21/*)";
const OTHER_DESC_31: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/31/*)";
const OTHER_DESC_41: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/41/*)";

fn main() -> Result<(), anyhow::Error> {
// Create a keyring with a single, default descriptor (aka the KeychainKind::External from the 1.2.0 API)
let mut keyring = KeyRing::new(EXTERNAL_DESC, Network::Signet);

// Add 3 new custom descriptors
keyring.add_other_descriptor(OTHER_DESC_21);
keyring.add_other_descriptor(OTHER_DESC_31);
keyring.add_other_descriptor(OTHER_DESC_41);

let keychain_ids: Vec<DescriptorId> = keyring.list_keychain_ids();
println!("{:?}", keychain_ids);

// Create the wallet and peek addresses on each of the descriptors
let mut wallet: Wallet = Wallet::create(keyring).create_wallet_no_persist()?;
let address_1 = wallet.peek_address(KeychainKind::Default, 0).unwrap();
let address_2 = wallet.peek_address(KeychainKind::Other(keychain_ids[1]), 0).unwrap();
let address_3 = wallet.peek_address(KeychainKind::Other(keychain_ids[2]), 0).unwrap();
let address_4 = wallet.peek_address(KeychainKind::Other(keychain_ids[3]), 0).unwrap();

println!("Address 1 {:?} at index {:?} on keychain {:?}", address_1.address, address_1.index, address_1.keychain);
println!("Address 2 {:?} at index {:?} on keychain {:?}", address_2.address, address_2.index, address_2.keychain);
println!("Address 3 {:?} at index {:?} on keychain {:?}", address_3.address, address_3.index, address_3.keychain);
println!("Address 4 {:?} at index {:?} on keychain {:?}", address_4.address, address_4.index, address_4.keychain);

let balance = wallet.balance();
println!("Balance before sync {:?}", balance);

let client = esplora_client::Builder::new(ESPLORA_URL).build_blocking();
let full_scan_request = wallet.start_full_scan().build();
let update = client.full_scan(full_scan_request, STOP_GAP, PARALLEL_REQUESTS)?;
wallet.apply_update(update)?;

let new_balance = wallet.balance();
println!("Wallet balance after syncing: {}", new_balance.total());

Ok(())
}
2 changes: 1 addition & 1 deletion wallet/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "bdk_wallet"
homepage = "https://bitcoindevkit.org"
version = "2.0.0-alpha.0"
version = "1.3.0-alpha.0"
repository = "https://github.com/bitcoindevkit/bdk_wallet"
documentation = "https://docs.rs/bdk_wallet"
description = "A modern, lightweight, descriptor-based wallet library"
Expand Down
48 changes: 48 additions & 0 deletions wallet/examples/n_keychains.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use bdk_chain::DescriptorId;
use bdk_wallet::Wallet;
use bdk_wallet::bitcoin::Network;
use bdk_wallet::KeychainKind;
use bdk_wallet::keyring::KeyRing;

const EXTERNAL_DESC: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/0/*)";
const OTHER_DESC_21: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/21/*)";
const OTHER_DESC_31: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/33/*)";
const OTHER_DESC_41: &str = "wpkh(tprv8ZgxMBicQKsPdy6LMhUtFHAgpocR8GC6QmwMSFpZs7h6Eziw3SpThFfczTDh5rW2krkqffa11UpX3XkeTTB2FvzZKWXqPY54Y6Rq4AQ5R8L/84'/1'/0'/44/*)";

fn main() -> Result<(), anyhow::Error> {
// Create a keyring with a single, default descriptor (aka the KeychainKind::External from the 1.2.0 API)
let mut keyring = KeyRing::new(EXTERNAL_DESC, Network::Testnet);

// Add 3 new custom descriptors
keyring.add_other_descriptor(OTHER_DESC_21);
keyring.add_other_descriptor(OTHER_DESC_31);
keyring.add_other_descriptor(OTHER_DESC_41);

let keychain_ids: Vec<DescriptorId> = keyring.list_keychain_ids();
println!("{:?}", keychain_ids);

// Create the wallet and peek addresses on each of the descriptors
let mut wallet: Wallet = Wallet::create(keyring).create_wallet_no_persist()?;
let address_1 = wallet.peek_address(KeychainKind::Default, 0).unwrap();
let address_2 = wallet.peek_address(KeychainKind::Other(keychain_ids[1]), 0).unwrap();
let address_3 = wallet.peek_address(KeychainKind::Other(keychain_ids[2]), 0).unwrap();
let address_4 = wallet.peek_address(KeychainKind::Other(keychain_ids[3]), 0).unwrap();

println!("Address 1 {:?} at index {:?} on keychain {:?}", address_1.address, address_1.index, address_1.keychain);
println!("Address 2 {:?} at index {:?} on keychain {:?}", address_2.address, address_2.index, address_2.keychain);
println!("Address 3 {:?} at index {:?} on keychain {:?}", address_3.address, address_3.index, address_3.keychain);
println!("Address 4 {:?} at index {:?} on keychain {:?}", address_4.address, address_4.index, address_4.keychain);

let balance = wallet.balance();
println!("Balance {:?}", balance);

let revealed_address_1 = wallet.reveal_next_address(KeychainKind::Default);
let revealed_address_2 = wallet.reveal_next_address(KeychainKind::Default);
println!("Revealed next address {:?}", revealed_address_1);
println!("Revealed next address {:?}", revealed_address_2);

// Will error out because there is no change keychain defined
// wallet.reveal_next_address(KeychainKind::Change).unwrap();

Ok(())
}
36 changes: 21 additions & 15 deletions wallet/src/descriptor/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,17 @@ use super::{ExtendedDescriptor, IntoWalletDescriptor, KeyMap};
use crate::descriptor::DescriptorError;
use crate::keys::{DerivableKey, IntoDescriptorKey, ValidNetworks};
use crate::wallet::utils::SecpCtx;
use crate::{descriptor, KeychainKind};
use crate::descriptor;

/// Type alias for the return type of [`DescriptorTemplate`], [`descriptor!`](crate::descriptor!) and others
pub type DescriptorTemplateOut = (ExtendedDescriptor, KeyMap, ValidNetworks);

#[derive(Clone, Debug)]
pub enum TemplateKeychainKind {
External,
Internal,
}

/// Trait for descriptor templates that can be built into a full descriptor
///
/// Since [`IntoWalletDescriptor`] is implemented for any [`DescriptorTemplate`], they can also be
Expand Down Expand Up @@ -232,7 +238,7 @@ impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip44<K: DerivableKey<Legacy>>(pub K, pub KeychainKind);
pub struct Bip44<K: DerivableKey<Legacy>>(pub K, pub TemplateKeychainKind);

impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
Expand Down Expand Up @@ -271,7 +277,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
pub struct Bip44Public<K: DerivableKey<Legacy>>(pub K, pub bip32::Fingerprint, pub TemplateKeychainKind);

impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
Expand Down Expand Up @@ -309,7 +315,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip49<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
pub struct Bip49<K: DerivableKey<Segwitv0>>(pub K, pub TemplateKeychainKind);

impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
Expand Down Expand Up @@ -348,7 +354,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
pub struct Bip49Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub TemplateKeychainKind);

impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
Expand Down Expand Up @@ -386,7 +392,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip84<K: DerivableKey<Segwitv0>>(pub K, pub KeychainKind);
pub struct Bip84<K: DerivableKey<Segwitv0>>(pub K, pub TemplateKeychainKind);

impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
Expand Down Expand Up @@ -425,7 +431,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
pub struct Bip84Public<K: DerivableKey<Segwitv0>>(pub K, pub bip32::Fingerprint, pub TemplateKeychainKind);

impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
Expand Down Expand Up @@ -463,7 +469,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip86<K: DerivableKey<Tap>>(pub K, pub KeychainKind);
pub struct Bip86<K: DerivableKey<Tap>>(pub K, pub TemplateKeychainKind);

impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
Expand Down Expand Up @@ -502,7 +508,7 @@ impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
#[derive(Debug, Clone)]
pub struct Bip86Public<K: DerivableKey<Tap>>(pub K, pub bip32::Fingerprint, pub KeychainKind);
pub struct Bip86Public<K: DerivableKey<Tap>>(pub K, pub bip32::Fingerprint, pub TemplateKeychainKind);

impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86Public<K> {
fn build(self, network: Network) -> Result<DescriptorTemplateOut, DescriptorError> {
Expand All @@ -521,7 +527,7 @@ macro_rules! expand_make_bipxx {
pub(super) fn make_bipxx_private<K: DerivableKey<$ctx>>(
bip: u32,
key: K,
keychain: KeychainKind,
keychain: TemplateKeychainKind,
network: Network,
) -> Result<impl IntoDescriptorKey<$ctx>, DescriptorError> {
let mut derivation_path = alloc::vec::Vec::with_capacity(4);
Expand All @@ -538,10 +544,10 @@ macro_rules! expand_make_bipxx {
derivation_path.push(bip32::ChildNumber::from_hardened_idx(0)?);

match keychain {
KeychainKind::External => {
TemplateKeychainKind::External => {
derivation_path.push(bip32::ChildNumber::from_normal_idx(0)?)
}
KeychainKind::Internal => {
TemplateKeychainKind::Internal => {
derivation_path.push(bip32::ChildNumber::from_normal_idx(1)?)
}
};
Expand All @@ -554,12 +560,12 @@ macro_rules! expand_make_bipxx {
bip: u32,
key: K,
parent_fingerprint: bip32::Fingerprint,
keychain: KeychainKind,
keychain: TemplateKeychainKind,
network: Network,
) -> Result<impl IntoDescriptorKey<$ctx>, DescriptorError> {
let derivation_path: bip32::DerivationPath = match keychain {
KeychainKind::External => vec![bip32::ChildNumber::from_normal_idx(0)?].into(),
KeychainKind::Internal => vec![bip32::ChildNumber::from_normal_idx(1)?].into(),
TemplateKeychainKind::External => vec![bip32::ChildNumber::from_normal_idx(0)?].into(),
TemplateKeychainKind::Internal => vec![bip32::ChildNumber::from_normal_idx(1)?].into(),
};

let source_path = bip32::DerivationPath::from(vec![
Expand Down
Loading
Loading