- TARGET=x86_64-unknown-linux-gnu CHECK_FMT=1
- TARGET=x86_64-unknown-linux-gnu RUN_TESTS=1
- TARGET=x86_64-unknown-linux-gnu FEATURES=minimal NO_DEFAULT_FEATURES=1
+ - TARGET=x86_64-unknown-linux-gnu FEATURES=all-keys NO_DEFAULT_FEATURES=1
- TARGET=x86_64-unknown-linux-gnu FEATURES=minimal,esplora NO_DEFAULT_FEATURES=1
- TARGET=x86_64-unknown-linux-gnu FEATURES=key-value-db NO_DEFAULT_FEATURES=1
- TARGET=x86_64-unknown-linux-gnu FEATURES=electrum NO_DEFAULT_FEATURES=1
rocksdb = { version = "0.14", optional = true }
socks = { version = "0.3", optional = true }
lazy_static = { version = "1.4", optional = true }
+tiny-bip39 = { version = "^0.7", optional = true }
[patch.crates-io]
bitcoin = { git = "https://github.com/rust-bitcoin/rust-bitcoin/", rev = "478e091" }
key-value-db = ["sled"]
cli-utils = ["clap", "base64"]
async-interface = ["async-trait"]
+all-keys = ["keys-bip39"]
+keys-bip39 = ["tiny-bip39"]
# Debug/Test features
debug-proc-macros = ["bdk-macros/debug", "bdk-testutils-macros/debug"]
( $descriptor_variant:ident, $key:expr ) => {{
use $crate::keys::ToDescriptorKey;
$key.to_descriptor_key()
- .into_key_and_secret()
+ .and_then(|key| key.into_key_and_secret())
.map(|(pk, key_map)| {
(
$crate::miniscript::Descriptor::<
/// assert_eq!(key_map_a.len(), key_map_b.len());
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
+///
+/// ------
+///
+/// Simple 2-of-2 multi-signature, equivalent to: `wsh(multi(2, ...))`
+///
+/// ```
+/// # use std::str::FromStr;
+/// let my_key_1 = bitcoin::PublicKey::from_str("02e96fe52ef0e22d2f131dd425ce1893073a3c6ad20e8cac36726393dfb4856a4c")?;
+/// let my_key_2 = bitcoin::PrivateKey::from_wif("cVt4o7BGAig1UXywgGSmARhxMdzP5qvQsxKkSsc1XEkw3tDTQFpy")?;
+///
+/// let (descriptor, key_map) = bdk::descriptor! {
+/// wsh (
+/// multi 2, my_key_1, my_key_2
+/// )
+/// }?;
+/// # Ok::<(), Box<dyn std::error::Error>>(())
+/// ```
#[macro_export]
macro_rules! descriptor {
( bare ( $( $minisc:tt )* ) ) => ({
});
( pk_k $key:expr ) => ({
use $crate::keys::ToDescriptorKey;
- $key.to_descriptor_key().into_key_and_secret()
+ $key.to_descriptor_key()
+ .and_then(|key| key.into_key_and_secret())
.and_then(|(pk, key_map)| Ok(($crate::impl_leaf_opcode_value!(PkK, pk)?.0, key_map)))
});
( pk $key:expr ) => ({
use $crate::keys::{ToDescriptorKey, DescriptorKey};
$keys.into_iter()
- .map(ToDescriptorKey::to_descriptor_key)
- .map(DescriptorKey::into_key_and_secret)
+ .map(|key| key.to_descriptor_key().and_then(DescriptorKey::into_key_and_secret))
.collect::<Result<Vec<_>, _>>()
.map(|items| items.into_iter().unzip())
.and_then(|(keys, key_maps): (Vec<_>, Vec<_>)| {
})
});
( multi $thresh:expr $(, $key:expr )+ ) => ({
+ use $crate::keys::ToDescriptorKey;
+
let mut keys = vec![];
$(
- keys.push($key);
+ keys.push($key.to_descriptor_key());
)*
- $crate::fragment!(multi_vec $thresh, keys)
+ keys.into_iter().collect::<Result<Vec<_>, _>>()
+ .and_then(|keys| $crate::fragment!(multi_vec $thresh, keys))
});
}
--- /dev/null
+// Magical Bitcoin Library
+// Written in 2020 by
+// Alekos Filini <alekos.filini@gmail.com>
+//
+// Copyright (c) 2020 Magical Bitcoin
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all
+// copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+// SOFTWARE.
+
+//! BIP-0039
+
+// TODO: maybe write our own implementation of bip39? Seems stupid to have an extra dependency for
+// something that should be fairly simple to re-implement.
+
+use bitcoin::util::bip32;
+use bitcoin::Network;
+
+use bip39::{Mnemonic, Seed};
+
+use super::{DescriptorKey, ToDescriptorKey};
+use crate::Error;
+
+pub type MnemonicWithPassphrase = (Mnemonic, Option<String>);
+
+impl ToDescriptorKey for (Seed, bip32::DerivationPath) {
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ let xprv = bip32::ExtendedPrivKey::new_master(Network::Bitcoin, &self.0.as_bytes())?;
+ (xprv, self.1).to_descriptor_key()
+ }
+}
+
+impl ToDescriptorKey for (MnemonicWithPassphrase, bip32::DerivationPath) {
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ let (mnemonic, passphrase) = self.0;
+ let seed = Seed::new(&mnemonic, passphrase.as_deref().unwrap_or(""));
+ (seed, self.1).to_descriptor_key()
+ }
+}
+
+impl ToDescriptorKey for (Mnemonic, bip32::DerivationPath) {
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ ((self.0, None), self.1).to_descriptor_key()
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::str::FromStr;
+
+ use bitcoin::util::bip32;
+
+ use bip39::{Language, Mnemonic};
+
+ #[test]
+ fn test_keys_bip39_mnemonic() {
+ let mnemonic =
+ "aim bunker wash balance finish force paper analyst cabin spoon stable organ";
+ let mnemonic = Mnemonic::from_phrase(mnemonic, Language::English).unwrap();
+ let path = bip32::DerivationPath::from_str("m/44'/0'/0'/0").unwrap();
+
+ let key = (mnemonic, path);
+ let (desc, keys) = crate::descriptor!(wpkh(key)).unwrap();
+ assert_eq!(desc.to_string(), "wpkh([be83839f/44'/0'/0']xpub6DCQ1YcqvZtSwGWMrwHELPehjWV3f2MGZ69yBADTxFEUAoLwb5Mp5GniQK6tTp3AgbngVz9zEFbBJUPVnkG7LFYt8QMTfbrNqs6FNEwAPKA/0/*)");
+ assert_eq!(keys.len(), 1);
+ }
+
+ #[test]
+ fn test_keys_bip39_mnemonic_passphrase() {
+ let mnemonic =
+ "aim bunker wash balance finish force paper analyst cabin spoon stable organ";
+ let mnemonic = Mnemonic::from_phrase(mnemonic, Language::English).unwrap();
+ let path = bip32::DerivationPath::from_str("m/44'/0'/0'/0").unwrap();
+
+ let key = ((mnemonic, Some("passphrase".into())), path);
+ let (desc, keys) = crate::descriptor!(wpkh(key)).unwrap();
+ assert_eq!(desc.to_string(), "wpkh([8f6cb80c/44'/0'/0']xpub6DWYS8bbihFevy29M4cbw4ZR3P5E12jB8R88gBDWCTCNpYiDHhYWNywrCF9VZQYagzPmsZpxXpytzSoxynyeFr4ZyzheVjnpLKuse4fiwZw/0/*)");
+ assert_eq!(keys.len(), 1);
+ }
+}
use crate::Error;
+#[cfg(feature = "keys-bip39")]
+#[cfg_attr(docsrs, doc(cfg(feature = "keys-bip39")))]
+pub mod bip39;
+
+/// Container for public or secret keys
pub enum DescriptorKey {
Public(DescriptorPublicKey),
Secret(DescriptorSecretKey),
}
}
+/// Trait for objects that can be turned into a public or secret [`DescriptorKey`]
pub trait ToDescriptorKey {
- fn to_descriptor_key(self) -> DescriptorKey;
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error>;
+}
+
+/// Identity conversion. This is used internally by [`bdk::fragment`]
+impl ToDescriptorKey for DescriptorKey {
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ Ok(self)
+ }
}
impl ToDescriptorKey for DescriptorPublicKey {
- fn to_descriptor_key(self) -> DescriptorKey {
- DescriptorKey::Public(self)
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ Ok(DescriptorKey::Public(self))
}
}
impl ToDescriptorKey for PublicKey {
- fn to_descriptor_key(self) -> DescriptorKey {
- DescriptorKey::Public(DescriptorPublicKey::PubKey(self))
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ Ok(DescriptorKey::Public(DescriptorPublicKey::PubKey(self)))
}
}
-impl ToDescriptorKey for (bip32::ExtendedPubKey, bip32::DerivationPath, bool) {
- fn to_descriptor_key(self) -> DescriptorKey {
- DescriptorKey::Public(DescriptorPublicKey::XPub(DescriptorXKey {
- source: None,
- xkey: self.0,
- derivation_path: self.1,
- is_wildcard: self.2,
- }))
+impl ToDescriptorKey for (bip32::ExtendedPubKey, bip32::DerivationPath) {
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ Ok(DescriptorKey::Public(DescriptorPublicKey::XPub(
+ DescriptorXKey {
+ source: None,
+ xkey: self.0,
+ derivation_path: self.1,
+ is_wildcard: true,
+ },
+ )))
}
}
impl ToDescriptorKey for DescriptorSecretKey {
- fn to_descriptor_key(self) -> DescriptorKey {
- DescriptorKey::Secret(self)
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ Ok(DescriptorKey::Secret(self))
}
}
impl ToDescriptorKey for PrivateKey {
- fn to_descriptor_key(self) -> DescriptorKey {
- DescriptorKey::Secret(DescriptorSecretKey::PrivKey(self))
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ Ok(DescriptorKey::Secret(DescriptorSecretKey::PrivKey(self)))
}
}
-impl ToDescriptorKey for (bip32::ExtendedPrivKey, bip32::DerivationPath, bool) {
- fn to_descriptor_key(self) -> DescriptorKey {
- DescriptorKey::Secret(DescriptorSecretKey::XPrv(DescriptorXKey {
- source: None,
- xkey: self.0,
- derivation_path: self.1,
- is_wildcard: self.2,
- }))
+impl ToDescriptorKey for (bip32::ExtendedPrivKey, bip32::DerivationPath) {
+ fn to_descriptor_key(self) -> Result<DescriptorKey, Error> {
+ Ok(DescriptorKey::Secret(DescriptorSecretKey::XPrv(
+ DescriptorXKey {
+ source: None,
+ xkey: self.0,
+ derivation_path: self.1,
+ is_wildcard: true,
+ },
+ )))
}
}