{ BDK deep dive }

@danielabrozzoni - MIT Bitcoin Expo 2023

About me

  • I'm @danielabrozzoni :)
  • Full time developer on the BitcoinDevKit project, supported by Spiral
  • Previously worked in Blockstream, Braiins, Revault

{ Bitcoin wallets }

What is a Bitcoin wallet?

  • Software used to manage keys and to make transactions on the Bitcoin network
  • Acts as a:
    • Keychain
    • Blockchain monitor
    • Transaction creator

Wallet as a keychain

  • Bitcoin wallets hold keys
  • Sometimes public keys only (watchonly wallets), sometimes private keys as well
  • Given the master key (either seed or xprv), need to generate the associated Bitcoin scripts

Wallet as a blockchain monitor

  • Periodically check if some coins has been sent to addresses managed by the wallet

Wallet as a transaction creator

  • Need to create transactions to let the users spend their money
  • Given a list of recipients and a feerate, find suitable inputs and construct the transaction, with an optional change
    • This step is called "coin selection"
  • Sign the transaction
  • Build the satisfying witness
  • Broadcast

{ Building }

{ Question: how hard is it to build a Bitcoin wallet from scratch? }

Building a wallet

  • Wallet acts as a
    • Keychain
    • Blockchain monitor
    • Transaction creator

Building a keychain

  • Usually wallets use a single "master" secret
  • Given the master secret, derive public keys from it (see BIP32)
  • For each public key, you build the associated script pubkey, which depends on the wallet type the user chose (P2PKH, P2SH, P2WPKH, P2WSH, P2TR, etc)

Building a blockchain monitor

  • Not *that* hard
  • Just write some code to ask to an external entity for updates
  • The "external entity" might be your own Bitcoin Core node, an esplora/electrum server, or Bitcoin Core nodes using the CBF protocol

Building a transaction creator

  • Figure out how expensive it is to spend each input
  • Coin selection
    • Various algorithms on how to select inputs given feerate & recipients
    • Not all algorithms are born equal (obviously)
    • Most famous are: branch and bound, single random draw, largest first, oldest first
  • Encode the transaction
  • Sign and construct the satisfying witness
  • Broadcast

{ Tools }

{ Building from scratch sounds difficult. Is there something out there that might help us? }

Picking a programming language

  • Type safety
  • Memory safety
  • Network effect
  • Cute mascot

rust-bitcoin

  • Provides Bitcoin-related primitives 
    • Transaction, TxOut, TxIn...
    • PrivateKey, PublicKey, Script, Address...
    • Block, BlockHeader
    • Message, Network, Address (IP)

rust-miniscript

  • Bitcoin Script is not easy to use, despite being really limited
  • Miniscript is a language for writing (a subset of) Bitcoin Scripts in a structured way 
  • Developer writes a policy, miniscript compiles it into a descriptor
  • Descriptor: an engineer-readable syntax for Bitcoin script 

rust-miniscript

<A> OP_CHECKSIGVERIFY <B> OP_CHECKSIG
and_v(v:pk(A),pk(B))
and(pk(A),pk(B))

rust-miniscript

  • Miniscript enables static analysis on descriptors
  • Given a descriptor, it is easy to:
    • Build a satisfying witness
    • Figure out the cheapest way to spend
    • Predict the cost of spending an input

BDK

  • A library to seamlessly build Bitcoin wallets
    • Makes it possible for non-Bitcoin experts to build Bitcoin wallets
  • Builds on top of rust-bitcoin, rust-miniscript
  • Highly customizable
    • How store data
    • How to sync the blockchain
  • Written in Rust, with bindings in Java/Kotlin, Python, Swift, and more coming soon :tm:

{ BDK }

{ Where we're at }

Main BDK structures

  • `Wallet`
    • Created from one or two descriptors
    • Provides methods to sync, get the balance, get new addresses, create/sign/broadcast transactions
  • `Database`
    • Used to cache info about the wallet (utxos, txs)
    • Currently supported: Sled, Sqlite, in-memory (useful for testing)
  • `Blockchain`
    • Used to sync the wallet
    • Currently supported: Bitcoin Core RPC, Electrum, Esplora, Compact Block filters

The BDK API (0.28)

let database = SqliteDatabase::new("path...");
let wallet = Wallet::new(
    "wpkh([c258d2e4/84h/1h/0h]tpub.../0/*)",
    Some("wpkh([c258d2e4/84h/1h/0h]tpub.../1/*)"),
    Network::Testnet,
    database,
)?;
let blockchain = EsploraBlockchain::from("url...", 50 /* stop_gap */);

wallet.sync(&blockchain, SyncOptions::default())?;

let send_to = Address::from_string("bc1q202rwadss5k6phxjazugjp87ncjmt62szj0ylk")?;
let (psbt, _) = {
    let mut builder =  wallet.build_tx();
    builder
        .add_recipient(send_to.script_pubkey(), 50_000)
        .enable_rbf()
        .current_height(786693)
        .fee_rate(FeeRate::from_sat_per_vb(5.0));
    builder.finish()?
};

println!("Unsigned PSBT: {}", &psbt);

The BDK API (0.28)

  • Good news: it works!
  • Bad news: it's not perfect

The BDK API - we can do better

  • Wallet is not thread safe (not Send/Sync)
    • Needs to be enclosed in a Mutex if you want to use it in multithread/async envs 
    • Some methods keep a lock on the Wallet for longer than necessary (ex: syncing)
    • Mobile apps usually cache balance/txs to be able to show them to the user while syncing

The BDK API - we can do better

  • Heavy use of traits in the public API
    • Rust traits are somewhat like Java interfaces
    • Designing the public interface before seeing how people might use it is hard, and leads to frequent API breaks down the road
    • The most obvious example of this is the syncing API in BDK

The BDK API - we can do better

  • Heavy use of traits in the public API
    • Example: Database trait defines what can be stored in the BDK database (`set_utxo`, `set_tx`, etc.)
    • Example: WalletSync trait defines how to sync the database against the blockchain, but it only lets you update the whole database in one shot
pub trait WalletSync {
    fn wallet_sync<D: BatchDatabase>(
        &self,
        database: &RefCell<D>,
        progress_update: Box<dyn Progress>
    ) -> Result<(), Error> { ... }
	// Other methods omitted for clarity
}

The BDK API - we can do better

  • Not modular
    • "Ohhh you want to leverage BDK's coin selection and completely ignore the rest? Too bad ¯\_(ツ)_/¯"
  • No no-std support

{ BDK }

{ Where we're going }

🎉 BDK 1.0 🎉

Eventually

Coming soon

I really don't know when

Sorry

Working on it

BDK 1.0: what

  • Stable API :)
  • Improved syncing API
    • No more `WalletSync` trait forcing us to sync everything in one shot
  • Modularization: divide BDK into multiple rust crates
    • Coin selection
    • Syncing primitives
  • Better support for multi-descriptor wallets
    • BIP47? 😏
  • no-std support

BDK 1.0: how

  • Halt development of bdk version 0
    • We release new versions of bdk 0 for critical bug fixes
    • Full team is working on bdk 1.0 at the moment
  • User feedback is extremely helpful
    • Consider trying it out and give feedback
    • Much ❤️ to mutiny wallet for already using bdk 1.0!

BDK 1.0: when

  • I really don't know
  • Soon! (Hopefully)

{ In conclusion }

  • Building wallets is hard
  • BDK makes it easier
  • BDK is cool, but we're making it even cooler

Thanks :)

{ bdk }

By Daniela Brozzoni