Skip to content

Commit 9855998

Browse files
committed
refactor(wallet)!: make internal descriptor optional in constructors
1 parent b978e16 commit 9855998

File tree

14 files changed

+179
-104
lines changed

14 files changed

+179
-104
lines changed

crates/hwi/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//!
2121
//! # let mut wallet = Wallet::new(
2222
//! # "",
23-
//! # "",
23+
//! # Some(""),
2424
//! # Network::Testnet,
2525
//! # )?;
2626
//! #

crates/wallet/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ let changeset = db.aggregate_changesets().expect("changeset loaded");
8282
let mut wallet = if let Some(changeset) = changeset {
8383
Wallet::load(changeset).expect("loaded wallet")
8484
} else {
85-
Wallet::new(descriptor, change_descriptor, Network::Testnet).expect("created new wallet")
85+
Wallet::new(descriptor, Some(change_descriptor), Network::Testnet).expect("created new wallet")
8686
};
8787
8888
// Get a new address to receive bitcoin.

crates/wallet/examples/compiler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ fn main() -> Result<(), Box<dyn Error>> {
7777
);
7878

7979
// Create a new wallet from descriptors
80-
let mut wallet = Wallet::new(&descriptor, &internal_descriptor, Network::Regtest)?;
80+
let mut wallet = Wallet::new(&descriptor, Some(&internal_descriptor), Network::Regtest)?;
8181

8282
println!(
8383
"First derived address from the descriptor: \n{}",

crates/wallet/src/descriptor/template.rs

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,11 @@ impl<T: DescriptorTemplate> IntoWalletDescriptor for T {
8181
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
8282
/// let key_internal =
8383
/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
84-
/// let mut wallet = Wallet::new(P2Pkh(key_external), P2Pkh(key_internal), Network::Testnet)?;
84+
/// let mut wallet = Wallet::new(
85+
/// P2Pkh(key_external),
86+
/// Some(P2Pkh(key_internal)),
87+
/// Network::Testnet,
88+
/// )?;
8589
///
8690
/// assert_eq!(
8791
/// wallet
@@ -115,7 +119,7 @@ impl<K: IntoDescriptorKey<Legacy>> DescriptorTemplate for P2Pkh<K> {
115119
/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
116120
/// let mut wallet = Wallet::new(
117121
/// P2Wpkh_P2Sh(key_external),
118-
/// P2Wpkh_P2Sh(key_internal),
122+
/// Some(P2Wpkh_P2Sh(key_internal)),
119123
/// Network::Testnet,
120124
/// )?;
121125
///
@@ -150,7 +154,11 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh_P2Sh<K> {
150154
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
151155
/// let key_internal =
152156
/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
153-
/// let mut wallet = Wallet::new(P2Wpkh(key_external), P2Wpkh(key_internal), Network::Testnet)?;
157+
/// let mut wallet = Wallet::new(
158+
/// P2Wpkh(key_external),
159+
/// Some(P2Wpkh(key_internal)),
160+
/// Network::Testnet,
161+
/// )?;
154162
///
155163
/// assert_eq!(
156164
/// wallet
@@ -182,7 +190,11 @@ impl<K: IntoDescriptorKey<Segwitv0>> DescriptorTemplate for P2Wpkh<K> {
182190
/// bitcoin::PrivateKey::from_wif("cTc4vURSzdx6QE6KVynWGomDbLaA75dNALMNyfjh3p8DRRar84Um")?;
183191
/// let key_internal =
184192
/// bitcoin::PrivateKey::from_wif("cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW")?;
185-
/// let mut wallet = Wallet::new(P2TR(key_external), P2TR(key_internal), Network::Testnet)?;
193+
/// let mut wallet = Wallet::new(
194+
/// P2TR(key_external),
195+
/// Some(P2TR(key_internal)),
196+
/// Network::Testnet,
197+
/// )?;
186198
///
187199
/// assert_eq!(
188200
/// wallet
@@ -217,7 +229,7 @@ impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
217229
/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
218230
/// let mut wallet = Wallet::new(
219231
/// Bip44(key.clone(), KeychainKind::External),
220-
/// Bip44(key, KeychainKind::Internal),
232+
/// Some(Bip44(key, KeychainKind::Internal)),
221233
/// Network::Testnet,
222234
/// )?;
223235
///
@@ -254,7 +266,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
254266
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
255267
/// let mut wallet = Wallet::new(
256268
/// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
257-
/// Bip44Public(key, fingerprint, KeychainKind::Internal),
269+
/// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)),
258270
/// Network::Testnet,
259271
/// )?;
260272
///
@@ -290,7 +302,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
290302
/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
291303
/// let mut wallet = Wallet::new(
292304
/// Bip49(key.clone(), KeychainKind::External),
293-
/// Bip49(key, KeychainKind::Internal),
305+
/// Some(Bip49(key, KeychainKind::Internal)),
294306
/// Network::Testnet,
295307
/// )?;
296308
///
@@ -327,7 +339,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
327339
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
328340
/// let mut wallet = Wallet::new(
329341
/// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
330-
/// Bip49Public(key, fingerprint, KeychainKind::Internal),
342+
/// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)),
331343
/// Network::Testnet,
332344
/// )?;
333345
///
@@ -363,7 +375,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
363375
/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
364376
/// let mut wallet = Wallet::new(
365377
/// Bip84(key.clone(), KeychainKind::External),
366-
/// Bip84(key, KeychainKind::Internal),
378+
/// Some(Bip84(key, KeychainKind::Internal)),
367379
/// Network::Testnet,
368380
/// )?;
369381
///
@@ -400,7 +412,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
400412
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
401413
/// let mut wallet = Wallet::new(
402414
/// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
403-
/// Bip84Public(key, fingerprint, KeychainKind::Internal),
415+
/// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)),
404416
/// Network::Testnet,
405417
/// )?;
406418
///
@@ -436,7 +448,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
436448
/// let key = bitcoin::bip32::Xpriv::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
437449
/// let mut wallet = Wallet::new(
438450
/// Bip86(key.clone(), KeychainKind::External),
439-
/// Bip86(key, KeychainKind::Internal),
451+
/// Some(Bip86(key, KeychainKind::Internal)),
440452
/// Network::Testnet,
441453
/// )?;
442454
///
@@ -473,7 +485,7 @@ impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
473485
/// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
474486
/// let mut wallet = Wallet::new(
475487
/// Bip86Public(key.clone(), fingerprint, KeychainKind::External),
476-
/// Bip86Public(key, fingerprint, KeychainKind::Internal),
488+
/// Some(Bip86Public(key, fingerprint, KeychainKind::Internal)),
477489
/// Network::Testnet,
478490
/// )?;
479491
///

crates/wallet/src/wallet/export.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
//! let import = FullyNodedExport::from_str(import)?;
3232
//! let wallet = Wallet::new(
3333
//! &import.descriptor(),
34-
//! &import.change_descriptor().expect("change descriptor"),
34+
//! Some(&import.change_descriptor().expect("change descriptor")),
3535
//! Network::Testnet,
3636
//! )?;
3737
//! # Ok::<_, Box<dyn std::error::Error>>(())
@@ -44,7 +44,7 @@
4444
//! # use bdk_wallet::*;
4545
//! let wallet = Wallet::new(
4646
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/0/*)",
47-
//! "wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)",
47+
//! Some("wpkh([c258d2e4/84h/1h/0h]tpubDD3ynpHgJQW8VvWRzQ5WFDCrs4jqVFGHB3vLC3r49XHJSqP8bHKdK4AriuUKLccK68zfzowx7YhmDN8SiSkgCDENUFx9qVw65YyqM78vyVe/1/*)"),
4848
//! Network::Testnet,
4949
//! )?;
5050
//! let export = FullyNodedExport::export_wallet(&wallet, "exported wallet", true).unwrap();
@@ -224,7 +224,7 @@ mod test {
224224
fn get_test_wallet(descriptor: &str, change_descriptor: &str, network: Network) -> Wallet {
225225
use crate::wallet::Update;
226226
use bdk_chain::TxGraph;
227-
let mut wallet = Wallet::new(descriptor, change_descriptor, network).unwrap();
227+
let mut wallet = Wallet::new(descriptor, Some(change_descriptor), network).unwrap();
228228
let transaction = Transaction {
229229
input: vec![],
230230
output: vec![],

crates/wallet/src/wallet/mod.rs

Lines changed: 61 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ impl Wallet {
294294
/// automatically to the new [`Wallet`].
295295
pub fn new<E: IntoWalletDescriptor>(
296296
descriptor: E,
297-
change_descriptor: E,
297+
change_descriptor: Option<E>,
298298
network: Network,
299299
) -> Result<Self, NewError> {
300300
let genesis_hash = genesis_block(network).block_hash();
@@ -307,7 +307,7 @@ impl Wallet {
307307
/// for syncing from alternative networks.
308308
pub fn new_with_genesis_hash<E: IntoWalletDescriptor>(
309309
descriptor: E,
310-
change_descriptor: E,
310+
change_descriptor: Option<E>,
311311
network: Network,
312312
genesis_hash: BlockHash,
313313
) -> Result<Self, NewError> {
@@ -1013,7 +1013,7 @@ impl Wallet {
10131013
/// # use bdk_wallet::bitcoin::Network;
10141014
/// let descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/0/*)";
10151015
/// let change_descriptor = "wpkh(tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/1'/0'/1/*)";
1016-
/// let wallet = Wallet::new(descriptor, change_descriptor, Network::Testnet)?;
1016+
/// let wallet = Wallet::new(descriptor, Some(change_descriptor), Network::Testnet)?;
10171017
/// for secret_key in wallet.get_signers(KeychainKind::External).signers().iter().filter_map(|s| s.descriptor_secret_key()) {
10181018
/// // secret_key: tprv8ZgxMBicQKsPe73PBRSmNbTfbcsZnwWhz5eVmhHpi31HW29Z7mc9B4cWGRQzopNUzZUT391DeDJxL2PefNunWyLgqCKRMDkU1s2s8bAfoSk/84'/0'/0'/0/*
10191019
/// println!("secret_key: {}", secret_key);
@@ -1072,14 +1072,10 @@ impl Wallet {
10721072
) -> Result<Psbt, CreateTxError> {
10731073
let keychains: BTreeMap<_, _> = self.indexed_graph.index.keychains().collect();
10741074
let external_descriptor = keychains.get(&KeychainKind::External).expect("must exist");
1075-
let internal_descriptor = keychains.get(&KeychainKind::Internal).expect("must exist");
10761075

10771076
let external_policy = external_descriptor
10781077
.extract_policy(&self.signers, BuildSatisfaction::None, &self.secp)?
10791078
.unwrap();
1080-
let internal_policy = internal_descriptor
1081-
.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
1082-
.unwrap();
10831079

10841080
// The policy allows spending external outputs, but it requires a policy path that hasn't been
10851081
// provided
@@ -1091,30 +1087,39 @@ impl Wallet {
10911087
KeychainKind::External,
10921088
));
10931089
};
1094-
// Same for the internal_policy path
1095-
if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
1096-
&& internal_policy.requires_path()
1097-
&& params.internal_policy_path.is_none()
1098-
{
1099-
return Err(CreateTxError::SpendingPolicyRequired(
1100-
KeychainKind::Internal,
1101-
));
1102-
};
11031090

11041091
let external_requirements = external_policy.get_condition(
11051092
params
11061093
.external_policy_path
11071094
.as_ref()
11081095
.unwrap_or(&BTreeMap::new()),
11091096
)?;
1110-
let internal_requirements = internal_policy.get_condition(
1111-
params
1112-
.internal_policy_path
1113-
.as_ref()
1114-
.unwrap_or(&BTreeMap::new()),
1115-
)?;
1097+
let mut requirements = external_requirements;
1098+
1099+
if let Some(internal_descriptor) = keychains.get(&KeychainKind::Internal) {
1100+
let internal_policy = internal_descriptor
1101+
.extract_policy(&self.change_signers, BuildSatisfaction::None, &self.secp)?
1102+
.unwrap();
11161103

1117-
let requirements = external_requirements.merge(&internal_requirements)?;
1104+
// Same for the internal_policy path
1105+
if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeForbidden
1106+
&& internal_policy.requires_path()
1107+
&& params.internal_policy_path.is_none()
1108+
{
1109+
return Err(CreateTxError::SpendingPolicyRequired(
1110+
KeychainKind::Internal,
1111+
));
1112+
};
1113+
1114+
let internal_requirements = internal_policy.get_condition(
1115+
params
1116+
.internal_policy_path
1117+
.as_ref()
1118+
.unwrap_or(&BTreeMap::new()),
1119+
)?;
1120+
1121+
requirements = requirements.merge(&internal_requirements)?;
1122+
}
11181123

11191124
let version = match params.version {
11201125
Some(tx_builder::Version(0)) => return Err(CreateTxError::Version0),
@@ -1280,12 +1285,18 @@ impl Wallet {
12801285
let drain_script = match params.drain_to {
12811286
Some(ref drain_recipient) => drain_recipient.clone(),
12821287
None => {
1283-
let change_keychain = KeychainKind::Internal;
1288+
let mut change_keychain = KeychainKind::Internal;
12841289
let ((index, spk), index_changeset) = self
12851290
.indexed_graph
12861291
.index
12871292
.next_unused_spk(&change_keychain)
1288-
.expect("keychain must exist");
1293+
.unwrap_or_else(|| {
1294+
change_keychain = KeychainKind::External;
1295+
self.indexed_graph
1296+
.index
1297+
.next_unused_spk(&change_keychain)
1298+
.expect("")
1299+
});
12891300
self.indexed_graph.index.mark_used(change_keychain, index);
12901301
self.stage.merge(index_changeset.into());
12911302
spk
@@ -1529,12 +1540,8 @@ impl Wallet {
15291540
if tx.output.len() > 1 {
15301541
let mut change_index = None;
15311542
for (index, txout) in tx.output.iter().enumerate() {
1532-
let change_keychain = KeychainKind::Internal;
1533-
match txout_index.index_of_spk(&txout.script_pubkey) {
1534-
Some((keychain, _)) if *keychain == change_keychain => {
1535-
change_index = Some(index)
1536-
}
1537-
_ => {}
1543+
if self.is_mine(&txout.script_pubkey) {
1544+
change_index = Some(index)
15381545
}
15391546
}
15401547

@@ -2322,32 +2329,38 @@ fn create_signers<E: IntoWalletDescriptor>(
23222329
index: &mut KeychainTxOutIndex<KeychainKind>,
23232330
secp: &Secp256k1<All>,
23242331
descriptor: E,
2325-
change_descriptor: E,
2332+
change_descriptor: Option<E>,
23262333
network: Network,
23272334
) -> Result<(Arc<SignersContainer>, Arc<SignersContainer>), DescriptorError> {
23282335
let descriptor = into_wallet_descriptor_checked(descriptor, secp, network)?;
2329-
let change_descriptor = into_wallet_descriptor_checked(change_descriptor, secp, network)?;
2336+
let change_descriptor = change_descriptor
2337+
.map(|cd| into_wallet_descriptor_checked(cd, secp, network))
2338+
.transpose()?;
23302339
let (descriptor, keymap) = descriptor;
23312340
let signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
23322341
let _ = index
23332342
.insert_descriptor(KeychainKind::External, descriptor)
23342343
.expect("this is the first descriptor we're inserting");
23352344

2336-
let (descriptor, keymap) = change_descriptor;
2337-
let change_signers = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
2338-
let _ = index
2339-
.insert_descriptor(KeychainKind::Internal, descriptor)
2340-
.map_err(|e| {
2341-
use bdk_chain::indexer::keychain_txout::InsertDescriptorError;
2342-
match e {
2343-
InsertDescriptorError::DescriptorAlreadyAssigned { .. } => {
2344-
crate::descriptor::error::Error::ExternalAndInternalAreTheSame
2345-
}
2346-
InsertDescriptorError::KeychainAlreadyAssigned { .. } => {
2347-
unreachable!("this is the first time we're assigning internal")
2345+
let change_signers = if let Some((descriptor, keymap)) = change_descriptor {
2346+
let change_signer = Arc::new(SignersContainer::build(keymap, &descriptor, secp));
2347+
let _ = index
2348+
.insert_descriptor(KeychainKind::Internal, descriptor)
2349+
.map_err(|e| {
2350+
use bdk_chain::indexer::keychain_txout::InsertDescriptorError;
2351+
match e {
2352+
InsertDescriptorError::DescriptorAlreadyAssigned { .. } => {
2353+
crate::descriptor::error::Error::ExternalAndInternalAreTheSame
2354+
}
2355+
InsertDescriptorError::KeychainAlreadyAssigned { .. } => {
2356+
unreachable!("this is the first time we're assigning internal")
2357+
}
23482358
}
2349-
}
2350-
})?;
2359+
})?;
2360+
change_signer
2361+
} else {
2362+
Arc::new(SignersContainer::new())
2363+
};
23512364

23522365
Ok((signers, change_signers))
23532366
}
@@ -2377,7 +2390,7 @@ macro_rules! doctest_wallet {
23772390

23782391
let mut wallet = Wallet::new(
23792392
descriptor,
2380-
change_descriptor,
2393+
Some(change_descriptor),
23812394
Network::Regtest,
23822395
)
23832396
.unwrap();

crates/wallet/src/wallet/signer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
//!
7070
//! let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/0/*)";
7171
//! let change_descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/1/*)";
72-
//! let mut wallet = Wallet::new(descriptor, change_descriptor, Network::Testnet)?;
72+
//! let mut wallet = Wallet::new(descriptor, Some(change_descriptor), Network::Testnet)?;
7373
//! wallet.add_signer(
7474
//! KeychainKind::External,
7575
//! SignerOrdering(200),

0 commit comments

Comments
 (0)