Release v0.2.0

By Alekos Filini

A new release of BDK is finally out! The v0.2.0 release contains many exciting new features, bug fixes and overall improvements. This release also marks the beginning of our new regular release schedule, which will see us pushing out a new release every four weeks. We think this is a good compromise to ensure that developers using BDK have access to all the new features and fixes as soon as possible, at least while the library is still evolving very fast as it is right now. After v1.0.0 we will increase this time to a more relaxed 6 weeks.

You can find the full v0.2.0 changelog on GitHub.

What’s new in v0.2.0

Considering the sheer amount of new things being added we don’t have room here to explain every new feature in detail, but below is a quick overview of some you could find useful in your projects.

A new name

The 0.1.0-beta.1 release was tagged right before the project was renamed bdk: at that time the library was still called “Magical Bitcoin Library”, or magical for short. With this release we have now renamed it to bdk. If you were using the library before, it should only be a matter of renaming the imports to match the new name. Alternatively you can also rename bdk to magical in your Cargo.toml, but you’ll still have to do some changes here and there because the APIs have been changed in a few places.

This release being particularly large contains a few different API-breaking changes: going forward we expect to make the interface more and more stable, which in turn will make applying updates easier.

Branch and Bound coin selection

We now support the state-of-the-art coin selection algorithm called “branch and bound”, with an implementation derived straight from Bitcoin Core. This algorithm is now enabled by default, but it can be replaced with a different one (either the old default, LargestFirstCoinSelection or a custom CoinSelectionAlgorithm) by using the TxBuilder::coin_selection() option.

Branch and bound works by trying to find a set of inputs that perfectly matches the amount being sent by a transaction, to avoid making an extra change output which takes up more space in the transaction, requires more fees, and in general lowers the privacy of a user if the change is later spent together with other outputs.

Key generation

If you need to generate a new bip32::ExtendedPrivKey, or perhaps a new BIP39 mnemonic, you can use the unified GeneratableKey trait to do so: paired with GeneratableDefaultOptions they provide many different ways to generate keys, with or without a custom source of entropy, and with or without customized options.

use bdk::bitcoin::PrivateKey;
use bdk::keys::{GeneratableKey, GeneratableDefaultOptions, PrivateKeyGenerateOptions};

let default_options_key = PrivateKey::generate_default()?;
let custom_options_key = PrivateKey::generate(PrivateKeyGenerateOptions { compressed: false })?;

Generic key types

With this update there’s now a generalized trait for keys that can be used in descriptors, which is called ToDescriptorKey. This trait is already implemented for the native rust-bitcoin key types, like PrivateKey, PublicKey, bip32::ExtendedPrivKey and bip32::ExtendedPubKey. It’s also implemented for BIP39 mnemonic and seeds, when the the opt-in keys-bip39 feature is enabled. As always, being this a public trait, you can also implement it for custom types to better suit your needs.

impl<Ctx: ScriptContext> ToDescriptorKey<Ctx> for MyKeyType {
    fn to_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
        // Custom conversion to `bitcoin::PrivateKey`
        let privkey: bitcoin::PrivateKey = ... ;
        privkey.to_descriptor_key()
    }
}

If your custom key type is simply a different representation of an xprv or xpub, you can also consider implementing the DerivableKey trait instead: for a type K that implements DerivableKey, the ToDescriptorKey trait is automatically implemented for the (K, bip32::DerivationPath) and (K, bip32::KeySource, bip32::DerivationPath) tuples.

impl<Ctx: ScriptContext> DerivableKey<Ctx> for MyKeyType {
     fn add_metadata(
        self, 
        origin: Option<KeySource>, 
        derivation_path: DerivationPath
    ) -> Result<DescriptorKey<Ctx>, KeyError> {
        // Custom conversion to `bip32::ExtendedPrivKey`
        let xprv: bip32::ExtendedPrivKey = ... ;
        xprv.add_metadata(origin, derivation_path)
    }
}

Descriptor templates

Instead of having to serialize keys to strings using format!() just to place them somewhere inside a descriptor, you can now use descriptor templates to build a descriptor starting from a key and some other options in a couple of lines of code. You can use one of the provided templates or make a custom one by implementing the DescriptorTemplate trait on a struct or enum.

let key = bip32::ExtendedPrivKey::from_str("...")?;
let wallet: OfflineWallet<_> = Wallet::new_offline(
    BIP84(key.clone(), KeychainKind::External),
    Some(BIP84(key, KeychainKind::Internal)),
    Network::Testnet,
    MemoryDatabase::default(),
)?;

Easier creation of Blockchain and Database

We’ve added a new way to create a Blockchain instance from a configuration, with the ConfigurableBlockchain trait. All the Blockchain types provided by the library implement this trait, which allows you to easily build an instance of them starting from a configuration struct: moreover, the configuration structures implement Serialize and Deserialize, so that they can be easily stored/loaded using serde.

We’ve also added a new Blockchain type called AnyBlockchain, which is essentially an enum that wraps all the Blockchain types exposed by the library. This allows you to build a Wallet that always has the same Rust type, but that can internally use different Blockchain backends chosen at runtime.

use bdk::blockchain::{AnyBlockchain, AnyBlockchainConfig, ConfigurableBlockchain, ElectrumBlockchainConfig};

let config = r#"{"Electrum":{"url":"ssl://electrum.blockstream.info:50002","socks5":null,"retry":3,"timeout":5}}"#;
let config = serde_json::from_str(config)?;
let blockchain = AnyBlockchain::from_config(&config)?;

The same is true for Database types, thanks to the ConfigurableDatabase trait and the AnyDatabase enum. While we think most people generally prefer to choose a single database type and then stick to it, it’s still good to offer the choice to switch them at runtime, should somebody need that.

descriptor!() macro

If you start writing complex descriptor templates, you’ll soon find yourself with the need of building large descriptor syntax trees: you can very easily do that with the descriptor!() macro, with the added bonus that some additional checks on the syntax of your descriptor will be performed at compile-time, rather than at runtime by. You can use any type that implements ToDescriptorKey (even strings!) as keys in pk(), multi() and sortedmulti() fragments, and you can even mix them in the same descriptor.

The syntax supported by the macro is almost exactly the same as the standard descriptor syntax we all know, with the only difference that modifiers should be specified individually rather than grouped in a series of characters (see the example below).

pub struct TimeDecayingMultisig<K> {
    pk_a: K,
    pk_b: K,
    timelock: u32,
}

impl<K: ToDescriptorKey<Segwitv0>> DescriptorTemplate for TimeDecayingMultisig<K> {
    fn build(self) -> Result<DescriptorTemplateOut, KeyError> {
        Ok(bdk::descriptor!(wsh(thresh(2,pk(self.pk_a),s:pk(self.pk_b),s:d:v:older(self.timelock))))
           .map_err(|e| KeyError::Message(e.to_string()))?)
    }
}

Support for sortedmulti()

Thanks to the addition of sortedmulti() in rust-miniscript, we can now also support them in BDK, which means we are getting more and more compatible with other descriptor-based wallets out there like Bitcoin Core.

Contributors

A huge thanks to everybody who contributed to this new release with suggestions, pull requests and bug reports.

Since the 0.1.0-beta.1 release over three months ago, we’ve had 213 new commits made by 10 different contributors for a total of 9990 additions and 2993 deletions. Here’s the full diff.

A special thanks to the 7 new contributors: