tr() descriptors
20d36c71d4Update CHANGELOG.md for Taproot (Alekos Filini)ef08fbd3c7Update to the newest release of rust-bitcoin (Alekos Filini)5320c8353etaproot-tests: validate `tap_tree` in psbt outputs (Alekos Filini)c1bfaf9b1eAdd blockchain tests for parsing, signing, finalizing taproot core psbts (Steve Myers)0643f76c1ftaproot-tests: Add tests for the policy module (Alekos Filini)89cb425e69taproot-tests: Add test coverage for tx signing (Alekos Filini)461397e590taproot-tests: Test taproot key and script spend in the blockchain tests (Alekos Filini)c67116fb55policy: Consider `tap_key_origins` when looking for sigs in PSBTs (Alekos Filini)572c3ee70dpolicy: Build `SatisfiableItem::*Signature` based on the context (Alekos Filini)ff1abc63e0policy: Refactor `PkOrF` into an enum (Alekos Filini)308708952bFix type inference for the `tr()` descriptor, add basic tests (Alekos Filini)fe1877fb18Support `tr()` descriptors in dsl (Alekos Filini)cdc7057813Add `tr()` descriptors to the `descriptor!()` macro (Alekos Filini)c121dd0252Use `tap_key_origins` in PSBTs to derive descriptors (Alekos Filini)8553821133Populate more taproot fields in PSBTs (Alekos Filini)8a5a87b075Populate `tap_key_origin` in PSBT inputs and outputs (Alekos Filini)1312184ed7Attach a context to our software signers (Alekos Filini)906598ad92Refactor signer traits, add support for taproot signatures (Alekos Filini) Pull request description: ### Description This is a work-in-progress PR to update BDK to rust-bitcoin `0.28` which introduces taproot support and a few other improvements. While updating we also introduce taproot support in BDK. High level list of subtasks for this PR: - [x] Update rust-bitcoin and rust-miniscript - [x] Stop using deprecated structs - [x] Add taproot metadata to psbts - [x] Produce schnorr signatures - [x] Finalize taproot txs - [x] Support `tr()` descriptors in the `descriptor!()` macro - [x] Write a lot of tests - [x] Interoperability with other wallets (Core + ?) - [x] Signing/finalizing a psbt made by core - [x] Producing a psbt that core can sign and finalize - [x] Creating psbts - [x] Verify the metadata are correct - [x] Verify sighashes are applied correctly - [x] Create a tx with a foreign taproot and non-taproot utxo - [x] Signing psbts - [x] Signing for a key spend - [x] Signing for a script spend - [x] Signing with a single (wif) key - [x] Signing with an xprv (with and without knowing the utxo being spent in the db) - [x] Signing with weird sighashes - [x] Policy module - [x] Simple key spend - [x] More complex tap tree with a few keys - [x] Verify both `contribution` and `satisfaction` of a PSBT input - [x] Wallet module - [x] Generate addresses Fixes #63 ### Notes to the reviewers #### Milestone I'm adding this to the `0.19` milestone because now that rust-bitcoin and rust-miniscript have been released we should not waiting too long to release a version of BDK that supports the new libraries. #### API Breaks Since this is an API-break because of the new version of rust-bitcoin and rust-miniscript, I'm also taking the chance to update a few things in our lib that I had been thinking about for a while. One example is the signer interface, which had that weird `sign_whole_tx()` method. This has now been removed, and the `Signer` trait replaced with `TransactionSigner` and `InputSigner`. I'm also starting to think that the signer should not only look at the psbt to figure out what to do, but ideally it should also receive some information about the descriptor (for example, the type) to simplify the code. One option is to add an extra parameter, but that would probably only be used by our internal signers and not much else (for example, if you ask an hardware wallet to sign, it will probably already know what kind of wallet you have). Another option is to wrap `PrivateKey` and `DescriptorXKey<ExtendedPrivKey>` which are the two internal signers we support with a struct that contains metadata about the descriptor, and then implement the signer traits on that struct. We could construct this in `Wallet::new()`, after miniscript parses the descriptor. #### MSRV Bump Due to the update of `rust-electrum-client`, which in turn depends on an updated `webpki`, we will have to bump our MSRV beacuse 1.46 is not supported by the new `webpki` version. ### Checklists #### All Submissions: * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing #### New Features: * [x] I've added docs for the new feature * [ ] I've updated `CHANGELOG.md` Top commit has no ACKs. Tree-SHA512: 44ec6c4e7fe0bc87862bb76581ddae7905c8796a4a130c90b48b73b4b7d01ea1a20043b8a0ff449fc2db2f7aecc490def8daee2d420809ded1ece7f085a48f55
BDK
A modern, lightweight, descriptor-based wallet library written in Rust!
Project Homepage | Documentation
About
The bdk library aims to be the core building block for Bitcoin wallets of any kind.
- It uses Miniscript to support descriptors with generalized conditions. This exact same library can be used to build single-sig wallets, multisigs, timelocked contracts and more.
- It supports multiple blockchain backends and databases, allowing developers to choose exactly what's right for their projects.
- It's built to be cross-platform: the core logic works on desktop, mobile, and even WebAssembly.
- It's very easy to extend: developers can implement customized logic for blockchain backends, databases, signers, coin selection, and more, without having to fork and modify this library.
Examples
Sync the balance of a descriptor
use bdk::Wallet;
use bdk::database::MemoryDatabase;
use bdk::blockchain::ElectrumBlockchain;
use bdk::SyncOptions;
use bdk::electrum_client::Client;
use bdk::bitcoin::Network;
fn main() -> Result<(), bdk::Error> {
let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);
let wallet = Wallet::new(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
Network::Testnet,
MemoryDatabase::default(),
)?;
wallet.sync(&blockchain, SyncOptions::default())?;
println!("Descriptor balance: {} SAT", wallet.get_balance()?);
Ok(())
}
Generate a few addresses
use bdk::{Wallet, database::MemoryDatabase};
use bdk::wallet::AddressIndex::New;
fn main() -> Result<(), bdk::Error> {
let wallet = Wallet::new(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
bitcoin::Network::Testnet,
MemoryDatabase::default(),
)?;
println!("Address #0: {}", wallet.get_address(New)?);
println!("Address #1: {}", wallet.get_address(New)?);
println!("Address #2: {}", wallet.get_address(New)?);
Ok(())
}
Create a transaction
use bdk::{FeeRate, Wallet, SyncOptions};
use bdk::database::MemoryDatabase;
use bdk::blockchain::ElectrumBlockchain;
use bdk::electrum_client::Client;
use bdk::wallet::AddressIndex::New;
use bitcoin::consensus::serialize;
fn main() -> Result<(), bdk::Error> {
let blockchain = ElectrumBlockchain::from(Client::new("ssl://electrum.blockstream.info:60002")?);
let wallet = Wallet::new(
"wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tpubDDYkZojQFQjht8Tm4jsS3iuEmKjTiEGjG6KnuFNKKJb5A6ZUCUZKdvLdSDWofKi4ToRCwb9poe1XdqfUnP4jaJjCB2Zwv11ZLgSbnZSNecE/1/*)"),
bitcoin::Network::Testnet,
MemoryDatabase::default(),
)?;
wallet.sync(&blockchain, SyncOptions::default())?;
let send_to = wallet.get_address(New)?;
let (psbt, details) = {
let mut builder = wallet.build_tx();
builder
.add_recipient(send_to.script_pubkey(), 50_000)
.enable_rbf()
.do_not_spend_change()
.fee_rate(FeeRate::from_sat_per_vb(5.0));
builder.finish()?
};
println!("Transaction details: {:#?}", details);
println!("Unsigned PSBT: {}", base64::encode(&serialize(&psbt)));
Ok(())
}
Sign a transaction
use bdk::{Wallet, SignOptions, database::MemoryDatabase};
use bitcoin::consensus::deserialize;
fn main() -> Result<(), bdk::Error> {
let wallet = Wallet::new(
"wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/0/*)",
Some("wpkh([c258d2e4/84h/1h/0h]tprv8griRPhA7342zfRyB6CqeKF8CJDXYu5pgnj1cjL1u2ngKcJha5jjTRimG82ABzJQ4MQe71CV54xfn25BbhCNfEGGJZnxvCDQCd6JkbvxW6h/1/*)"),
bitcoin::Network::Testnet,
MemoryDatabase::default(),
)?;
let psbt = "...";
let mut psbt = deserialize(&base64::decode(psbt).unwrap())?;
let finalized = wallet.sign(&mut psbt, SignOptions::default())?;
Ok(())
}
Testing
Unit testing
cargo test
Integration testing
Integration testing require testing features, for example:
cargo test --features test-electrum
The other options are test-esplora or test-rpc.
Note that electrs and bitcoind binaries are automatically downloaded (on mac and linux), to specify you already have installed binaries you must use --no-default-features and provide BITCOIND_EXE and ELECTRS_EXE as environment variables.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.