build!: Update bdk to rust-bitcoin 0.30.0

This commit is contained in:
Daniela Brozzoni 2023-08-09 15:36:45 +02:00
parent 0ba6bbe114
commit 958e72877c
No known key found for this signature in database
GPG Key ID: 7DE4F1FDCED0AB87
42 changed files with 765 additions and 527 deletions

View File

@ -14,16 +14,16 @@ license = "MIT OR Apache-2.0"
[dependencies] [dependencies]
bdk-macros = "^0.6" bdk-macros = "^0.6"
log = "0.4" log = "0.4"
miniscript = { version = "9.0", default-features = false, features = ["serde"] } miniscript = { version = "10.0", default-features = false, features = ["serde"] }
bitcoin = { version = "0.29.2", default-features = false, features = ["serde", "base64", "rand"] } bitcoin = { version = "0.30", default-features = false, features = ["serde", "base64", "rand-std"] }
serde = { version = "^1.0", features = ["derive"] } serde = { version = "^1.0", features = ["derive"] }
serde_json = { version = "^1.0" } serde_json = { version = "^1.0" }
rand = "^0.8" rand = "^0.8"
# Optional dependencies # Optional dependencies
sled = { version = "0.34", optional = true } sled = { version = "0.34", optional = true }
electrum-client = { version = "0.12", optional = true } electrum-client = { version = "0.18", optional = true }
esplora-client = { version = "0.4", default-features = false, optional = true } esplora-client = { version = "0.6", default-features = false, optional = true }
rusqlite = { version = "0.28.0", optional = true } rusqlite = { version = "0.28.0", optional = true }
ahash = { version = "0.7.6", optional = true } ahash = { version = "0.7.6", optional = true }
futures = { version = "0.3", optional = true } futures = { version = "0.3", optional = true }
@ -31,13 +31,13 @@ async-trait = { version = "0.1", optional = true }
rocksdb = { version = "0.14", default-features = false, features = ["snappy"], optional = true } rocksdb = { version = "0.14", default-features = false, features = ["snappy"], optional = true }
cc = { version = ">=1.0.64", optional = true } cc = { version = ">=1.0.64", optional = true }
socks = { version = "0.3", optional = true } socks = { version = "0.3", optional = true }
hwi = { version = "0.5", optional = true, features = ["use-miniscript"] } hwi = { version = "0.7", optional = true, features = ["miniscript"] }
bip39 = { version = "2.0.0", optional = true } bip39 = { version = "2.0.0", optional = true }
bitcoinconsensus = { version = "0.19.0-3", optional = true } bitcoinconsensus = { version = "0.19.0-3", optional = true }
# Needed by bdk_blockchain_tests macro and the `rpc` feature # Needed by bdk_blockchain_tests macro and the `rpc` feature
bitcoincore-rpc = { version = "0.16", optional = true } bitcoincore-rpc = { package="core-rpc", version = "0.17", optional = true }
# Platform-specific dependencies # Platform-specific dependencies
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
@ -107,13 +107,11 @@ test-hardware-signer = ["hardware-signer"]
dev-getrandom-wasm = ["getrandom/js"] dev-getrandom-wasm = ["getrandom/js"]
[dev-dependencies] [dev-dependencies]
miniscript = { version = "9.0", features = ["std"] } miniscript = { version = "10.0", features = ["std"] }
bitcoin = { version = "0.29.2", features = ["std"] } bitcoin = { version = "0.30", features = ["std"] }
lazy_static = "1.4" lazy_static = "1.4"
env_logger = { version = "0.7", default-features = false } env_logger = { version = "0.7", default-features = false }
electrsd = "0.22" electrsd = "0.24"
# Remove after upgrade to rust-bitcoin ^0.30 where base64 is re-exported
base64 = "^0.13"
assert_matches = "1.5.0" assert_matches = "1.5.0"
[[example]] [[example]]

View File

@ -96,7 +96,7 @@ use bdk::blockchain::ElectrumBlockchain;
use bdk::electrum_client::Client; use bdk::electrum_client::Client;
use bdk::wallet::AddressIndex::New; use bdk::wallet::AddressIndex::New;
use base64; use bitcoin::base64;
use bdk::bitcoin::consensus::serialize; use bdk::bitcoin::consensus::serialize;
use bdk::bitcoin::Network; use bdk::bitcoin::Network;
@ -123,7 +123,7 @@ fn main() -> Result<(), bdk::Error> {
}; };
println!("Transaction details: {:#?}", details); println!("Transaction details: {:#?}", details);
println!("Unsigned PSBT: {}", base64::encode(&serialize(&psbt))); println!("Unsigned PSBT: {}", base64::encode(psbt.serialize()));
Ok(()) Ok(())
} }
@ -134,9 +134,9 @@ fn main() -> Result<(), bdk::Error> {
```rust,no_run ```rust,no_run
use bdk::{Wallet, SignOptions, database::MemoryDatabase}; use bdk::{Wallet, SignOptions, database::MemoryDatabase};
use base64; use bitcoin::base64;
use bdk::bitcoin::consensus::deserialize; use bdk::bitcoin::consensus::deserialize;
use bdk::bitcoin::Network; use bdk::bitcoin::{psbt::Psbt, Network};
fn main() -> Result<(), bdk::Error> { fn main() -> Result<(), bdk::Error> {
let wallet = Wallet::new( let wallet = Wallet::new(
@ -147,7 +147,7 @@ fn main() -> Result<(), bdk::Error> {
)?; )?;
let psbt = "..."; let psbt = "...";
let mut psbt = deserialize(&base64::decode(psbt).unwrap())?; let mut psbt = Psbt::deserialize(&base64::decode(psbt).unwrap())?;
let _finalized = wallet.sign(&mut psbt, SignOptions::default())?; let _finalized = wallet.sign(&mut psbt, SignOptions::default())?;

View File

@ -1,6 +1,6 @@
use std::str::FromStr; use std::str::FromStr;
use bdk::bitcoin::util::bip32::ExtendedPrivKey; use bdk::bitcoin::bip32::ExtendedPrivKey;
use bdk::bitcoin::Network; use bdk::bitcoin::Network;
use bdk::blockchain::{Blockchain, ElectrumBlockchain}; use bdk::blockchain::{Blockchain, ElectrumBlockchain};
use bdk::database::MemoryDatabase; use bdk::database::MemoryDatabase;
@ -10,7 +10,7 @@ use bdk::{KeychainKind, SyncOptions, Wallet};
use bdk::electrum_client::Client; use bdk::electrum_client::Client;
use bdk::wallet::AddressIndex; use bdk::wallet::AddressIndex;
use bitcoin::util::bip32; use bitcoin::bip32;
pub mod utils; pub mod utils;

View File

@ -9,7 +9,7 @@ use bdk::{
KeychainKind, SyncOptions, Wallet, KeychainKind, SyncOptions, Wallet,
}; };
use bitcoin::{ use bitcoin::{
util::bip32::{self, ExtendedPrivKey}, bip32::{self, ExtendedPrivKey},
Network, Network,
}; };

View File

@ -9,7 +9,7 @@ use bdk::{
KeychainKind, SyncOptions, Wallet, KeychainKind, SyncOptions, Wallet,
}; };
use bitcoin::{ use bitcoin::{
util::bip32::{self, ExtendedPrivKey}, bip32::{self, ExtendedPrivKey},
Network, Network,
}; };

View File

@ -1,7 +1,7 @@
use bdk::bitcoin::{Address, Network}; use bdk::bitcoin::{Address, Network};
use bdk::blockchain::{Blockchain, ElectrumBlockchain}; use bdk::blockchain::{Blockchain, ElectrumBlockchain};
use bdk::database::MemoryDatabase; use bdk::database::MemoryDatabase;
use bdk::hwi::{types::HWIChain, HWIClient}; use bdk::hwi::HWIClient;
use bdk::miniscript::{Descriptor, DescriptorPublicKey}; use bdk::miniscript::{Descriptor, DescriptorPublicKey};
use bdk::signer::SignerOrdering; use bdk::signer::SignerOrdering;
use bdk::wallet::{hardwaresigner::HWISigner, AddressIndex}; use bdk::wallet::{hardwaresigner::HWISigner, AddressIndex};
@ -30,7 +30,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
let first_device = devices.remove(0)?; let first_device = devices.remove(0)?;
// ...and creating a client out of the first one // ...and creating a client out of the first one
let client = HWIClient::get_client(&first_device, true, HWIChain::Test)?; let client = HWIClient::get_client(&first_device, true, Network::Testnet.into())?;
println!("Look what I found, a {}!", first_device.model); println!("Look what I found, a {}!", first_device.model);
// Getting the HW's public descriptors // Getting the HW's public descriptors
@ -41,7 +41,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
); );
// Creating a custom signer from the device // Creating a custom signer from the device
let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?; let custom_signer = HWISigner::from_device(&first_device, Network::Testnet.into())?;
let mut wallet = Wallet::new( let mut wallet = Wallet::new(
descriptors.receive[0].clone(), descriptors.receive[0].clone(),
Some(descriptors.internal[0].clone()), Some(descriptors.internal[0].clone()),
@ -77,7 +77,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
return Ok(()); return Ok(());
} }
let return_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?; let return_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?
.require_network(Network::Testnet)?;
let (mut psbt, _details) = { let (mut psbt, _details) = {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder

View File

@ -6,8 +6,8 @@
// You may not use this file except in accordance with one or both of these // You may not use this file except in accordance with one or both of these
// licenses. // licenses.
use bdk::bitcoin::bip32::DerivationPath;
use bdk::bitcoin::secp256k1::Secp256k1; use bdk::bitcoin::secp256k1::Secp256k1;
use bdk::bitcoin::util::bip32::DerivationPath;
use bdk::bitcoin::Network; use bdk::bitcoin::Network;
use bdk::descriptor; use bdk::descriptor;
use bdk::descriptor::IntoWalletDescriptor; use bdk::descriptor::IntoWalletDescriptor;

View File

@ -92,7 +92,8 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
} else { } else {
println!("Creating a PSBT sending 9800 SATs plus fee to the u01.net testnet faucet return address 'tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt'."); println!("Creating a PSBT sending 9800 SATs plus fee to the u01.net testnet faucet return address 'tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt'.");
let return_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?; let return_address = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")?
.require_network(Network::Testnet)?;
let mut builder = watch_only_wallet.build_tx(); let mut builder = watch_only_wallet.build_tx();
builder builder
.add_recipient(return_address.script_pubkey(), 9_800) .add_recipient(return_address.script_pubkey(), 9_800)

View File

@ -62,7 +62,10 @@ fn main() -> Result<(), Box<dyn Error>> {
}; };
// Get a new core address // Get a new core address
let core_address = bitcoind.client.get_new_address(None, None)?; let core_address = bitcoind
.client
.get_new_address(None, None)?
.require_network(Network::Regtest)?;
// Generate 101 blocks and use the above address as coinbase // Generate 101 blocks and use the above address as coinbase
bitcoind.client.generate_to_address(101, &core_address)?; bitcoind.client.generate_to_address(101, &core_address)?;

View File

@ -13,7 +13,10 @@ pub(crate) mod tx {
// Create a transaction builder // Create a transaction builder
let mut tx_builder = wallet.build_tx(); let mut tx_builder = wallet.build_tx();
let to_address = Address::from_str(recipient_address).unwrap(); let to_address = Address::from_str(recipient_address)
.unwrap()
.require_network(wallet.network())
.unwrap();
// Set recipient of the transaction // Set recipient of the transaction
tx_builder.set_recipients(vec![(to_address.script_pubkey(), amount)]); tx_builder.set_recipients(vec![(to_address.script_pubkey(), amount)]);

View File

@ -355,7 +355,7 @@ impl WalletSync for CompactFiltersBlockchain {
peer, peer,
|block_hash, filter| { |block_hash, filter| {
if !filter if !filter
.match_any(block_hash, &mut all_scripts.iter().map(AsRef::as_ref))? .match_any(block_hash, all_scripts.iter().map(|s| s.as_slice()))?
{ {
return Ok(false); return Ok(false);
} }
@ -570,7 +570,7 @@ pub enum CompactFiltersError {
/// Internal I/O error /// Internal I/O error
Io(std::io::Error), Io(std::io::Error),
/// Invalid BIP158 filter /// Invalid BIP158 filter
Bip158(bitcoin::util::bip158::Error), Bip158(bitcoin::bip158::Error),
/// Internal system time error /// Internal system time error
Time(std::time::SystemTimeError), Time(std::time::SystemTimeError),
@ -608,7 +608,7 @@ impl std::error::Error for CompactFiltersError {}
impl_error!(rocksdb::Error, Db, CompactFiltersError); impl_error!(rocksdb::Error, Db, CompactFiltersError);
impl_error!(std::io::Error, Io, CompactFiltersError); impl_error!(std::io::Error, Io, CompactFiltersError);
impl_error!(bitcoin::util::bip158::Error, Bip158, CompactFiltersError); impl_error!(bitcoin::bip158::Error, Bip158, CompactFiltersError);
impl_error!(std::time::SystemTimeError, Time, CompactFiltersError); impl_error!(std::time::SystemTimeError, Time, CompactFiltersError);
impl From<crate::error::Error> for CompactFiltersError { impl From<crate::error::Error> for CompactFiltersError {

View File

@ -27,7 +27,7 @@ use bitcoin::network::message::{NetworkMessage, RawNetworkMessage};
use bitcoin::network::message_blockdata::*; use bitcoin::network::message_blockdata::*;
use bitcoin::network::message_filter::*; use bitcoin::network::message_filter::*;
use bitcoin::network::message_network::VersionMessage; use bitcoin::network::message_network::VersionMessage;
use bitcoin::network::Address; use bitcoin::network::{Address, Magic};
use bitcoin::{Block, Network, Transaction, Txid, Wtxid}; use bitcoin::{Block, Network, Transaction, Txid, Wtxid};
use super::CompactFiltersError; use super::CompactFiltersError;
@ -242,7 +242,7 @@ impl Peer {
/// Send a Bitcoin network message /// Send a Bitcoin network message
fn _send( fn _send(
writer: &mut TcpStream, writer: &mut TcpStream,
magic: u32, magic: Magic,
payload: NetworkMessage, payload: NetworkMessage,
) -> Result<(), CompactFiltersError> { ) -> Result<(), CompactFiltersError> {
log::trace!("==> {:?}", payload); log::trace!("==> {:?}", payload);

View File

@ -21,16 +21,17 @@ use rand::{thread_rng, Rng};
use rocksdb::{Direction, IteratorMode, ReadOptions, WriteBatch, DB}; use rocksdb::{Direction, IteratorMode, ReadOptions, WriteBatch, DB};
use bitcoin::bip158::BlockFilter;
use bitcoin::block::Header;
use bitcoin::blockdata::constants::genesis_block; use bitcoin::blockdata::constants::genesis_block;
use bitcoin::consensus::{deserialize, encode::VarInt, serialize, Decodable, Encodable}; use bitcoin::consensus::{deserialize, encode::VarInt, serialize, Decodable, Encodable};
use bitcoin::hash_types::{FilterHash, FilterHeader}; use bitcoin::hash_types::{FilterHash, FilterHeader};
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use bitcoin::util::bip158::BlockFilter; use bitcoin::pow::Work;
use bitcoin::util::uint::Uint256;
use bitcoin::Block; use bitcoin::Block;
use bitcoin::BlockHash; use bitcoin::BlockHash;
use bitcoin::BlockHeader;
use bitcoin::Network; use bitcoin::Network;
use bitcoin::ScriptBuf;
use super::CompactFiltersError; use super::CompactFiltersError;
@ -69,7 +70,7 @@ impl StoreEntry {
} }
StoreEntry::Block(Some(height)) => prefix.extend_from_slice(&height.to_be_bytes()), StoreEntry::Block(Some(height)) => prefix.extend_from_slice(&height.to_be_bytes()),
StoreEntry::BlockHeaderIndex(Some(hash)) => { StoreEntry::BlockHeaderIndex(Some(hash)) => {
prefix.extend_from_slice(&hash.into_inner()) prefix.extend_from_slice(hash.to_raw_hash().as_ref())
} }
StoreEntry::CFilterTable((filter_type, bundle_index)) => { StoreEntry::CFilterTable((filter_type, bundle_index)) => {
prefix.push(*filter_type); prefix.push(*filter_type);
@ -228,7 +229,7 @@ impl ChainStore<Full> {
batch.put_cf( batch.put_cf(
cf_handle, cf_handle,
genesis_key, genesis_key,
(genesis.header, genesis.header.work()).serialize(), (genesis.header, genesis.header.work().to_be_bytes()).serialize(),
); );
batch.put_cf( batch.put_cf(
cf_handle, cf_handle,
@ -260,7 +261,7 @@ impl ChainStore<Full> {
step *= 2; step *= 2;
} }
let (header, _): (BlockHeader, Uint256) = SerializeDb::deserialize( let (header, _): (Header, [u8; 32]) = SerializeDb::deserialize(
&store_read &store_read
.get_pinned_cf(cf_handle, StoreEntry::BlockHeader(Some(index)).get_key())? .get_pinned_cf(cf_handle, StoreEntry::BlockHeader(Some(index)).get_key())?
.unwrap(), .unwrap(),
@ -292,11 +293,12 @@ impl ChainStore<Full> {
let cf_handle = write_store.cf_handle(&self.cf_name).unwrap(); let cf_handle = write_store.cf_handle(&self.cf_name).unwrap();
let new_cf_handle = write_store.cf_handle(&new_cf_name).unwrap(); let new_cf_handle = write_store.cf_handle(&new_cf_name).unwrap();
let (header, work): (BlockHeader, Uint256) = SerializeDb::deserialize( let (header, work): (Header, [u8; 32]) = SerializeDb::deserialize(
&write_store &write_store
.get_pinned_cf(cf_handle, StoreEntry::BlockHeader(Some(from)).get_key())? .get_pinned_cf(cf_handle, StoreEntry::BlockHeader(Some(from)).get_key())?
.ok_or(CompactFiltersError::DataCorruption)?, .ok_or(CompactFiltersError::DataCorruption)?,
)?; )?;
let work = Work::from_be_bytes(work);
let mut batch = WriteBatch::default(); let mut batch = WriteBatch::default();
batch.put_cf( batch.put_cf(
@ -307,7 +309,7 @@ impl ChainStore<Full> {
batch.put_cf( batch.put_cf(
new_cf_handle, new_cf_handle,
StoreEntry::BlockHeader(Some(from)).get_key(), StoreEntry::BlockHeader(Some(from)).get_key(),
(header, work).serialize(), (header, work.to_be_bytes()).serialize(),
); );
write_store.write(batch)?; write_store.write(batch)?;
@ -381,7 +383,7 @@ impl ChainStore<Full> {
opts, opts,
IteratorMode::From(&from_key, Direction::Forward), IteratorMode::From(&from_key, Direction::Forward),
) { ) {
let (header, _): (BlockHeader, Uint256) = SerializeDb::deserialize(&v)?; let (header, _): (Header, [u8; 32]) = SerializeDb::deserialize(&v)?;
batch.delete_cf( batch.delete_cf(
cf_handle, cf_handle,
@ -433,7 +435,7 @@ impl ChainStore<Full> {
let key = StoreEntry::BlockHeader(Some(height)).get_key(); let key = StoreEntry::BlockHeader(Some(height)).get_key();
let data = read_store.get_pinned_cf(cf_handle, key)?; let data = read_store.get_pinned_cf(cf_handle, key)?;
data.map(|data| { data.map(|data| {
let (header, _): (BlockHeader, Uint256) = let (header, _): (Header, [u8; 32]) =
deserialize(&data).map_err(|_| CompactFiltersError::DataCorruption)?; deserialize(&data).map_err(|_| CompactFiltersError::DataCorruption)?;
Ok::<_, CompactFiltersError>(header.block_hash()) Ok::<_, CompactFiltersError>(header.block_hash())
}) })
@ -496,7 +498,7 @@ impl ChainStore<Full> {
} }
impl<T: StoreType> ChainStore<T> { impl<T: StoreType> ChainStore<T> {
pub fn work(&self) -> Result<Uint256, CompactFiltersError> { pub fn work(&self) -> Result<Work, CompactFiltersError> {
let read_store = self.store.read().unwrap(); let read_store = self.store.read().unwrap();
let cf_handle = read_store.cf_handle(&self.cf_name).unwrap(); let cf_handle = read_store.cf_handle(&self.cf_name).unwrap();
@ -506,12 +508,13 @@ impl<T: StoreType> ChainStore<T> {
Ok(iterator Ok(iterator
.last() .last()
.map(|(_, v)| -> Result<_, CompactFiltersError> { .map(|(_, v)| -> Result<_, CompactFiltersError> {
let (_, work): (BlockHeader, Uint256) = SerializeDb::deserialize(&v)?; let (_, work): (Header, [u8; 32]) = SerializeDb::deserialize(&v)?;
let work = Work::from_be_bytes(work);
Ok(work) Ok(work)
}) })
.transpose()? .transpose()?
.unwrap_or_default()) .unwrap_or_else(|| Work::from_be_bytes([0; 32])))
} }
pub fn get_height(&self) -> Result<usize, CompactFiltersError> { pub fn get_height(&self) -> Result<usize, CompactFiltersError> {
@ -546,7 +549,7 @@ impl<T: StoreType> ChainStore<T> {
iterator iterator
.last() .last()
.map(|(_, v)| -> Result<_, CompactFiltersError> { .map(|(_, v)| -> Result<_, CompactFiltersError> {
let (header, _): (BlockHeader, Uint256) = SerializeDb::deserialize(&v)?; let (header, _): (Header, [u8; 32]) = SerializeDb::deserialize(&v)?;
Ok(header.block_hash()) Ok(header.block_hash())
}) })
@ -556,7 +559,7 @@ impl<T: StoreType> ChainStore<T> {
pub fn apply( pub fn apply(
&mut self, &mut self,
from: usize, from: usize,
headers: Vec<BlockHeader>, headers: Vec<Header>,
) -> Result<BlockHash, CompactFiltersError> { ) -> Result<BlockHash, CompactFiltersError> {
let mut batch = WriteBatch::default(); let mut batch = WriteBatch::default();
@ -566,7 +569,8 @@ impl<T: StoreType> ChainStore<T> {
let (mut last_hash, mut accumulated_work) = read_store let (mut last_hash, mut accumulated_work) = read_store
.get_pinned_cf(cf_handle, StoreEntry::BlockHeader(Some(from)).get_key())? .get_pinned_cf(cf_handle, StoreEntry::BlockHeader(Some(from)).get_key())?
.map(|result| { .map(|result| {
let (header, work): (BlockHeader, Uint256) = SerializeDb::deserialize(&result)?; let (header, work): (Header, [u8; 32]) = SerializeDb::deserialize(&result)?;
let work = Work::from_be_bytes(work);
Ok::<_, CompactFiltersError>((header.block_hash(), work)) Ok::<_, CompactFiltersError>((header.block_hash(), work))
}) })
.transpose()? .transpose()?
@ -589,7 +593,7 @@ impl<T: StoreType> ChainStore<T> {
batch.put_cf( batch.put_cf(
cf_handle, cf_handle,
StoreEntry::BlockHeader(Some(height)).get_key(), StoreEntry::BlockHeader(Some(height)).get_key(),
(header, accumulated_work).serialize(), (header, accumulated_work.to_be_bytes()).serialize(),
); );
} }
@ -641,7 +645,7 @@ impl CfStore {
let genesis = genesis_block(headers_store.network); let genesis = genesis_block(headers_store.network);
let filter = BlockFilter::new_script_filter(&genesis, |utxo| { let filter = BlockFilter::new_script_filter(&genesis, |utxo| {
Err(bitcoin::util::bip158::Error::UtxoMissing(*utxo)) Err::<ScriptBuf, _>(bitcoin::bip158::Error::UtxoMissing(*utxo))
})?; })?;
let first_key = StoreEntry::CFilterTable((filter_type, Some(0))).get_key(); let first_key = StoreEntry::CFilterTable((filter_type, Some(0))).get_key();
@ -653,7 +657,7 @@ impl CfStore {
&first_key, &first_key,
( (
BundleStatus::Init, BundleStatus::Init,
filter.filter_header(&FilterHeader::from_hash(Hash::all_zeros())), filter.filter_header(&FilterHeader::from_raw_hash(Hash::all_zeros())),
) )
.serialize(), .serialize(),
)?; )?;

View File

@ -13,11 +13,11 @@ use std::collections::{BTreeMap, HashMap, VecDeque};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use std::time::Duration; use std::time::Duration;
use bitcoin::bip158::BlockFilter;
use bitcoin::hash_types::{BlockHash, FilterHeader}; use bitcoin::hash_types::{BlockHash, FilterHeader};
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use bitcoin::network::message::NetworkMessage; use bitcoin::network::message::NetworkMessage;
use bitcoin::network::message_blockdata::GetHeadersMessage; use bitcoin::network::message_blockdata::GetHeadersMessage;
use bitcoin::util::bip158::BlockFilter;
use super::peer::*; use super::peer::*;
use super::store::*; use super::store::*;

View File

@ -325,8 +325,8 @@ impl ConfigurableBlockchain for ElectrumBlockchain {
let socks5 = config.socks5.as_ref().map(Socks5Config::new); let socks5 = config.socks5.as_ref().map(Socks5Config::new);
let electrum_config = ConfigBuilder::new() let electrum_config = ConfigBuilder::new()
.retry(config.retry) .retry(config.retry)
.timeout(config.timeout)? .timeout(config.timeout)
.socks5(socks5)? .socks5(socks5)
.validate_domain(config.validate_domain) .validate_domain(config.validate_domain)
.build(); .build();

View File

@ -132,7 +132,7 @@ impl WalletSync for EsploraBlockchain {
let scripts = script_req let scripts = script_req
.request() .request()
.take(self.concurrency as usize) .take(self.concurrency as usize)
.cloned(); .map(bitcoin::ScriptBuf::from);
let mut handles = vec![]; let mut handles = vec![];
for script in scripts { for script in scripts {

View File

@ -31,18 +31,17 @@
//! let blockchain = RpcBlockchain::from_config(&config); //! let blockchain = RpcBlockchain::from_config(&config);
//! ``` //! ```
use crate::bitcoin::hashes::hex::ToHex;
use crate::bitcoin::{Network, OutPoint, Transaction, TxOut, Txid}; use crate::bitcoin::{Network, OutPoint, Transaction, TxOut, Txid};
use crate::blockchain::*; use crate::blockchain::*;
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils}; use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
use crate::descriptor::calc_checksum; use crate::descriptor::calc_checksum;
use crate::error::MissingCachedScripts; use crate::error::MissingCachedScripts;
use crate::{BlockTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails}; use crate::{BlockTime, Error, FeeRate, KeychainKind, LocalUtxo, TransactionDetails};
use bitcoin::Script; use bitcoin::{Script, ScriptBuf};
use bitcoincore_rpc::json::{ use bitcoincore_rpc::json::{
GetTransactionResultDetailCategory, ImportMultiOptions, ImportMultiRequest, GetTransactionResultDetailCategory, ImportMultiOptions, ImportMultiRequest,
ImportMultiRequestScriptPubkey, ImportMultiRescanSince, ListTransactionResult, ImportMultiRequestScriptPubkey, ListTransactionResult, ListUnspentResultEntry, ScanningDetails,
ListUnspentResultEntry, ScanningDetails, Timestamp,
}; };
use bitcoincore_rpc::jsonrpc::serde_json::{json, Value}; use bitcoincore_rpc::jsonrpc::serde_json::{json, Value};
use bitcoincore_rpc::Auth as RpcAuth; use bitcoincore_rpc::Auth as RpcAuth;
@ -302,8 +301,8 @@ struct DbState<'a, D> {
params: &'a RpcSyncParams, params: &'a RpcSyncParams,
prog: &'a dyn Progress, prog: &'a dyn Progress,
ext_spks: Vec<Script>, ext_spks: Vec<ScriptBuf>,
int_spks: Vec<Script>, int_spks: Vec<ScriptBuf>,
txs: HashMap<Txid, TransactionDetails>, txs: HashMap<Txid, TransactionDetails>,
utxos: HashSet<LocalUtxo>, utxos: HashSet<LocalUtxo>,
last_indexes: HashMap<KeychainKind, u32>, last_indexes: HashMap<KeychainKind, u32>,
@ -668,7 +667,7 @@ fn import_descriptors<'a, S>(
scripts_iter: S, scripts_iter: S,
) -> Result<(), Error> ) -> Result<(), Error>
where where
S: Iterator<Item = &'a Script>, S: Iterator<Item = &'a ScriptBuf>,
{ {
let requests = Value::Array( let requests = Value::Array(
scripts_iter scripts_iter
@ -696,11 +695,11 @@ where
fn import_multi<'a, S>(client: &Client, start_epoch: u64, scripts_iter: S) -> Result<(), Error> fn import_multi<'a, S>(client: &Client, start_epoch: u64, scripts_iter: S) -> Result<(), Error>
where where
S: Iterator<Item = &'a Script>, S: Iterator<Item = &'a ScriptBuf>,
{ {
let requests = scripts_iter let requests = scripts_iter
.map(|script| ImportMultiRequest { .map(|script| ImportMultiRequest {
timestamp: ImportMultiRescanSince::Timestamp(start_epoch), timestamp: Timestamp::Time(start_epoch),
script_pubkey: Some(ImportMultiRequestScriptPubkey::Script(script)), script_pubkey: Some(ImportMultiRequestScriptPubkey::Script(script)),
watchonly: Some(true), watchonly: Some(true),
..Default::default() ..Default::default()
@ -808,7 +807,7 @@ fn is_wallet_descriptor(client: &Client) -> Result<bool, Error> {
} }
fn descriptor_from_script_pubkey(script: &Script) -> String { fn descriptor_from_script_pubkey(script: &Script) -> String {
let desc = format!("raw({})", script.to_hex()); let desc = format!("raw({})", script.to_hex_string());
format!("{}#{}", desc, calc_checksum(&desc).unwrap()) format!("{}#{}", desc, calc_checksum(&desc).unwrap())
} }
@ -964,7 +963,7 @@ mod test {
// generate scripts (1 tx per script) // generate scripts (1 tx per script)
let scripts = (0..TX_COUNT) let scripts = (0..TX_COUNT)
.map(|index| desc.at_derivation_index(index).script_pubkey()) .map(|index| desc.at_derivation_index(index).unwrap().script_pubkey())
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// import scripts and wait // import scripts and wait

View File

@ -9,7 +9,7 @@ use crate::{
wallet::time::Instant, wallet::time::Instant,
BlockTime, Error, KeychainKind, LocalUtxo, TransactionDetails, BlockTime, Error, KeychainKind, LocalUtxo, TransactionDetails,
}; };
use bitcoin::{OutPoint, Script, Transaction, TxOut, Txid}; use bitcoin::{OutPoint, Script, ScriptBuf, Transaction, TxOut, Txid};
use log::*; use log::*;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque}; use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
@ -53,7 +53,7 @@ pub struct ScriptReq<'a, D: BatchDatabase> {
state: State<'a, D>, state: State<'a, D>,
script_index: usize, script_index: usize,
initial_scripts_needed: usize, // if this is 1, we assume the descriptor is not derivable initial_scripts_needed: usize, // if this is 1, we assume the descriptor is not derivable
scripts_needed: VecDeque<Script>, scripts_needed: VecDeque<ScriptBuf>,
stop_gap: usize, stop_gap: usize,
keychain: KeychainKind, keychain: KeychainKind,
next_keychains: Vec<KeychainKind>, next_keychains: Vec<KeychainKind>,
@ -62,7 +62,7 @@ pub struct ScriptReq<'a, D: BatchDatabase> {
/// The sync starts by returning script pubkeys we are interested in. /// The sync starts by returning script pubkeys we are interested in.
impl<'a, D: BatchDatabase> ScriptReq<'a, D> { impl<'a, D: BatchDatabase> ScriptReq<'a, D> {
pub fn request(&self) -> impl Iterator<Item = &Script> + Clone { pub fn request(&self) -> impl Iterator<Item = &Script> + Clone {
self.scripts_needed.iter() self.scripts_needed.iter().map(|s| s.as_script())
} }
pub fn satisfy( pub fn satisfy(

View File

@ -153,7 +153,7 @@ impl BatchOperations for AnyDatabase {
&mut self, &mut self,
keychain: KeychainKind, keychain: KeychainKind,
child: u32, child: u32,
) -> Result<Option<Script>, Error> { ) -> Result<Option<ScriptBuf>, Error> {
impl_inner_method!( impl_inner_method!(
AnyDatabase, AnyDatabase,
self, self,
@ -204,7 +204,7 @@ impl Database for AnyDatabase {
) )
} }
fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<Script>, Error> { fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<ScriptBuf>, Error> {
impl_inner_method!(AnyDatabase, self, iter_script_pubkeys, keychain) impl_inner_method!(AnyDatabase, self, iter_script_pubkeys, keychain)
} }
fn iter_utxos(&self) -> Result<Vec<LocalUtxo>, Error> { fn iter_utxos(&self) -> Result<Vec<LocalUtxo>, Error> {
@ -221,7 +221,7 @@ impl Database for AnyDatabase {
&self, &self,
keychain: KeychainKind, keychain: KeychainKind,
child: u32, child: u32,
) -> Result<Option<Script>, Error> { ) -> Result<Option<ScriptBuf>, Error> {
impl_inner_method!( impl_inner_method!(
AnyDatabase, AnyDatabase,
self, self,
@ -286,7 +286,7 @@ impl BatchOperations for AnyBatch {
&mut self, &mut self,
keychain: KeychainKind, keychain: KeychainKind,
child: u32, child: u32,
) -> Result<Option<Script>, Error> { ) -> Result<Option<ScriptBuf>, Error> {
impl_inner_method!(AnyBatch, self, del_script_pubkey_from_path, keychain, child) impl_inner_method!(AnyBatch, self, del_script_pubkey_from_path, keychain, child)
} }
fn del_path_from_script_pubkey( fn del_path_from_script_pubkey(

View File

@ -15,7 +15,7 @@ use sled::{Batch, Tree};
use bitcoin::consensus::encode::{deserialize, serialize}; use bitcoin::consensus::encode::{deserialize, serialize};
use bitcoin::hash_types::Txid; use bitcoin::hash_types::Txid;
use bitcoin::{OutPoint, Script, Transaction}; use bitcoin::{OutPoint, Script, ScriptBuf, Transaction};
use crate::database::memory::MapKey; use crate::database::memory::MapKey;
use crate::database::{BatchDatabase, BatchOperations, Database, SyncTime}; use crate::database::{BatchDatabase, BatchOperations, Database, SyncTime};
@ -90,7 +90,7 @@ macro_rules! impl_batch_operations {
Ok(()) Ok(())
} }
fn del_script_pubkey_from_path(&mut self, keychain: KeychainKind, path: u32) -> Result<Option<Script>, Error> { fn del_script_pubkey_from_path(&mut self, keychain: KeychainKind, path: u32) -> Result<Option<ScriptBuf>, Error> {
let key = MapKey::Path((Some(keychain), Some(path))).as_map_key(); let key = MapKey::Path((Some(keychain), Some(path))).as_map_key();
let res = self.remove(key); let res = self.remove(key);
let res = $process_delete!(res); let res = $process_delete!(res);
@ -221,7 +221,7 @@ impl Database for Tree {
} }
} }
fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<Script>, Error> { fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<ScriptBuf>, Error> {
let key = MapKey::Path((keychain, None)).as_map_key(); let key = MapKey::Path((keychain, None)).as_map_key();
self.scan_prefix(key) self.scan_prefix(key)
.map(|x| -> Result<_, Error> { .map(|x| -> Result<_, Error> {
@ -286,7 +286,7 @@ impl Database for Tree {
&self, &self,
keychain: KeychainKind, keychain: KeychainKind,
path: u32, path: u32,
) -> Result<Option<Script>, Error> { ) -> Result<Option<ScriptBuf>, Error> {
let key = MapKey::Path((Some(keychain), Some(path))).as_map_key(); let key = MapKey::Path((Some(keychain), Some(path))).as_map_key();
Ok(self.get(key)?.map(|b| deserialize(&b)).transpose()?) Ok(self.get(key)?.map(|b| deserialize(&b)).transpose()?)
} }

View File

@ -20,7 +20,7 @@ use std::ops::Bound::{Excluded, Included};
use bitcoin::consensus::encode::{deserialize, serialize}; use bitcoin::consensus::encode::{deserialize, serialize};
use bitcoin::hash_types::Txid; use bitcoin::hash_types::Txid;
use bitcoin::{OutPoint, Script, Transaction}; use bitcoin::{OutPoint, Script, ScriptBuf, Transaction};
use crate::database::{BatchDatabase, BatchOperations, ConfigurableDatabase, Database, SyncTime}; use crate::database::{BatchDatabase, BatchOperations, ConfigurableDatabase, Database, SyncTime};
use crate::error::Error; use crate::error::Error;
@ -136,7 +136,7 @@ impl BatchOperations for MemoryDatabase {
path: u32, path: u32,
) -> Result<(), Error> { ) -> Result<(), Error> {
let key = MapKey::Path((Some(keychain), Some(path))).as_map_key(); let key = MapKey::Path((Some(keychain), Some(path))).as_map_key();
self.map.insert(key, Box::new(script.clone())); self.map.insert(key, Box::new(ScriptBuf::from(script)));
let key = MapKey::Script(Some(script)).as_map_key(); let key = MapKey::Script(Some(script)).as_map_key();
let value = json!({ let value = json!({
@ -196,7 +196,7 @@ impl BatchOperations for MemoryDatabase {
&mut self, &mut self,
keychain: KeychainKind, keychain: KeychainKind,
path: u32, path: u32,
) -> Result<Option<Script>, Error> { ) -> Result<Option<ScriptBuf>, Error> {
let key = MapKey::Path((Some(keychain), Some(path))).as_map_key(); let key = MapKey::Path((Some(keychain), Some(path))).as_map_key();
let res = self.map.remove(&key); let res = self.map.remove(&key);
self.deleted_keys.push(key); self.deleted_keys.push(key);
@ -315,7 +315,7 @@ impl Database for MemoryDatabase {
} }
} }
fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<Script>, Error> { fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<ScriptBuf>, Error> {
let key = MapKey::Path((keychain, None)).as_map_key(); let key = MapKey::Path((keychain, None)).as_map_key();
self.map self.map
.range::<Vec<u8>, _>((Included(&key), Excluded(&after(&key)))) .range::<Vec<u8>, _>((Included(&key), Excluded(&after(&key))))
@ -368,7 +368,7 @@ impl Database for MemoryDatabase {
&self, &self,
keychain: KeychainKind, keychain: KeychainKind,
path: u32, path: u32,
) -> Result<Option<Script>, Error> { ) -> Result<Option<ScriptBuf>, Error> {
let key = MapKey::Path((Some(keychain), Some(path))).as_map_key(); let key = MapKey::Path((Some(keychain), Some(path))).as_map_key();
Ok(self Ok(self
.map .map
@ -485,7 +485,6 @@ macro_rules! populate_test_db {
$crate::populate_test_db!($db, $tx_meta, $current_height, (@coinbase false)) $crate::populate_test_db!($db, $tx_meta, $current_height, (@coinbase false))
}}; }};
($db:expr, $tx_meta:expr, $current_height:expr, (@coinbase $is_coinbase:expr)$(,)?) => {{ ($db:expr, $tx_meta:expr, $current_height:expr, (@coinbase $is_coinbase:expr)$(,)?) => {{
use std::str::FromStr;
use $crate::database::SyncTime; use $crate::database::SyncTime;
use $crate::database::{BatchOperations, Database}; use $crate::database::{BatchOperations, Database};
let mut db = $db; let mut db = $db;
@ -497,7 +496,7 @@ macro_rules! populate_test_db {
} }
let tx = $crate::bitcoin::Transaction { let tx = $crate::bitcoin::Transaction {
version: 1, version: 1,
lock_time: bitcoin::PackedLockTime(0), lock_time: bitcoin::absolute::LockTime::ZERO,
input, input,
output: tx_meta output: tx_meta
.output .output
@ -506,6 +505,7 @@ macro_rules! populate_test_db {
value: out_meta.value, value: out_meta.value,
script_pubkey: $crate::bitcoin::Address::from_str(&out_meta.to_address) script_pubkey: $crate::bitcoin::Address::from_str(&out_meta.to_address)
.unwrap() .unwrap()
.assume_checked()
.script_pubkey(), .script_pubkey(),
}) })
.collect(), .collect(),

View File

@ -27,7 +27,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use bitcoin::hash_types::Txid; use bitcoin::hash_types::Txid;
use bitcoin::{OutPoint, Script, Transaction, TxOut}; use bitcoin::{OutPoint, Script, ScriptBuf, Transaction, TxOut};
use crate::error::Error; use crate::error::Error;
use crate::types::*; use crate::types::*;
@ -83,7 +83,7 @@ pub trait BatchOperations {
&mut self, &mut self,
keychain: KeychainKind, keychain: KeychainKind,
child: u32, child: u32,
) -> Result<Option<Script>, Error>; ) -> Result<Option<ScriptBuf>, Error>;
/// Delete the data related to a specific script_pubkey, meaning the keychain and the child /// Delete the data related to a specific script_pubkey, meaning the keychain and the child
/// number. /// number.
fn del_path_from_script_pubkey( fn del_path_from_script_pubkey(
@ -124,7 +124,7 @@ pub trait Database: BatchOperations {
) -> Result<(), Error>; ) -> Result<(), Error>;
/// Return the list of script_pubkeys /// Return the list of script_pubkeys
fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<Script>, Error>; fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<ScriptBuf>, Error>;
/// Return the list of [`LocalUtxo`]s /// Return the list of [`LocalUtxo`]s
fn iter_utxos(&self) -> Result<Vec<LocalUtxo>, Error>; fn iter_utxos(&self) -> Result<Vec<LocalUtxo>, Error>;
/// Return the list of raw transactions /// Return the list of raw transactions
@ -137,7 +137,7 @@ pub trait Database: BatchOperations {
&self, &self,
keychain: KeychainKind, keychain: KeychainKind,
child: u32, child: u32,
) -> Result<Option<Script>, Error>; ) -> Result<Option<ScriptBuf>, Error>;
/// Fetch the keychain and child number of a given script_pubkey /// Fetch the keychain and child number of a given script_pubkey
fn get_path_from_script_pubkey( fn get_path_from_script_pubkey(
&self, &self,
@ -214,17 +214,17 @@ impl<T: Database> DatabaseUtils for T {}
#[cfg(test)] #[cfg(test)]
pub mod test { pub mod test {
use std::str::FromStr;
use bitcoin::consensus::encode::deserialize; use bitcoin::consensus::encode::deserialize;
use bitcoin::consensus::serialize; use bitcoin::consensus::serialize;
use bitcoin::hashes::hex::*; use bitcoin::hashes::hex::*;
use bitcoin::Witness;
use bitcoin::*; use bitcoin::*;
use std::str::FromStr;
use super::*; use super::*;
pub fn test_script_pubkey<D: Database>(mut db: D) { pub fn test_script_pubkey<D: Database>(mut db: D) {
let script = Script::from( let script = ScriptBuf::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(), Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
); );
let path = 42; let path = 42;
@ -245,7 +245,7 @@ pub mod test {
pub fn test_batch_script_pubkey<D: BatchDatabase>(mut db: D) { pub fn test_batch_script_pubkey<D: BatchDatabase>(mut db: D) {
let mut batch = db.begin_batch(); let mut batch = db.begin_batch();
let script = Script::from( let script = ScriptBuf::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(), Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
); );
let path = 42; let path = 42;
@ -272,7 +272,7 @@ pub mod test {
} }
pub fn test_iter_script_pubkey<D: Database>(mut db: D) { pub fn test_iter_script_pubkey<D: Database>(mut db: D) {
let script = Script::from( let script = ScriptBuf::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(), Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
); );
let path = 42; let path = 42;
@ -284,7 +284,7 @@ pub mod test {
} }
pub fn test_del_script_pubkey<D: Database>(mut db: D) { pub fn test_del_script_pubkey<D: Database>(mut db: D) {
let script = Script::from( let script = ScriptBuf::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(), Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
); );
let path = 42; let path = 42;
@ -302,7 +302,7 @@ pub mod test {
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:0", "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:0",
) )
.unwrap(); .unwrap();
let script = Script::from( let script = ScriptBuf::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(), Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
); );
let txout = TxOut { let txout = TxOut {
@ -478,7 +478,7 @@ pub mod test {
pub fn test_del_path_from_script_pubkey<D: Database>(mut db: D) { pub fn test_del_path_from_script_pubkey<D: Database>(mut db: D) {
let keychain = KeychainKind::External; let keychain = KeychainKind::External;
let script = Script::from( let script = ScriptBuf::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(), Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
); );
let path = 42; let path = 42;
@ -502,14 +502,14 @@ pub mod test {
let scripts = db.iter_script_pubkeys(Some(keychain)).unwrap(); let scripts = db.iter_script_pubkeys(Some(keychain)).unwrap();
assert!(scripts.is_empty()); assert!(scripts.is_empty());
let first_script = Script::from( let first_script = ScriptBuf::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(), Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
); );
let path = 42; let path = 42;
db.set_script_pubkey(&first_script, keychain, path).unwrap(); db.set_script_pubkey(&first_script, keychain, path).unwrap();
let second_script = Script::from( let second_script = ScriptBuf::from(
Vec::<u8>::from_hex("00145c9a1816d38db5cbdd4b067b689dc19eb7d930e2").unwrap(), Vec::<u8>::from_hex("00145c9a1816d38db5cbdd4b067b689dc19eb7d930e2").unwrap(),
); );
let path = 57; let path = 57;
@ -528,7 +528,7 @@ pub mod test {
"5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:0", "5df6e0e2761359d30a8275058e299fcc0381534545f55cf43e41983f5d4c9456:0",
) )
.unwrap(); .unwrap();
let script = Script::from( let script = ScriptBuf::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(), Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
); );
let txout = TxOut { let txout = TxOut {

View File

@ -13,7 +13,7 @@ use std::path::PathBuf;
use bitcoin::consensus::encode::{deserialize, serialize}; use bitcoin::consensus::encode::{deserialize, serialize};
use bitcoin::hash_types::Txid; use bitcoin::hash_types::Txid;
use bitcoin::{OutPoint, Script, Transaction, TxOut}; use bitcoin::{OutPoint, Script, ScriptBuf, Transaction, TxOut};
use crate::database::{BatchDatabase, BatchOperations, Database, SyncTime}; use crate::database::{BatchDatabase, BatchOperations, Database, SyncTime};
use crate::error::Error; use crate::error::Error;
@ -162,7 +162,7 @@ impl SqliteDatabase {
None => (None, None), None => (None, None),
}; };
let txid: &[u8] = &transaction.txid; let txid: &[u8] = transaction.txid.as_ref();
let mut statement = self.connection.prepare_cached("INSERT INTO transaction_details (txid, timestamp, received, sent, fee, height) VALUES (:txid, :timestamp, :received, :sent, :fee, :height)")?; let mut statement = self.connection.prepare_cached("INSERT INTO transaction_details (txid, timestamp, received, sent, fee, height) VALUES (:txid, :timestamp, :received, :sent, :fee, :height)")?;
@ -187,7 +187,7 @@ impl SqliteDatabase {
None => (None, None), None => (None, None),
}; };
let txid: &[u8] = &transaction.txid; let txid: &[u8] = transaction.txid.as_ref();
let mut statement = self.connection.prepare_cached("UPDATE transaction_details SET timestamp=:timestamp, received=:received, sent=:sent, fee=:fee, height=:height WHERE txid=:txid")?; let mut statement = self.connection.prepare_cached("UPDATE transaction_details SET timestamp=:timestamp, received=:received, sent=:sent, fee=:fee, height=:height WHERE txid=:txid")?;
@ -254,11 +254,11 @@ impl SqliteDatabase {
Ok(self.connection.last_insert_rowid()) Ok(self.connection.last_insert_rowid())
} }
fn select_script_pubkeys(&self) -> Result<Vec<Script>, Error> { fn select_script_pubkeys(&self) -> Result<Vec<ScriptBuf>, Error> {
let mut statement = self let mut statement = self
.connection .connection
.prepare_cached("SELECT script FROM script_pubkeys")?; .prepare_cached("SELECT script FROM script_pubkeys")?;
let mut scripts: Vec<Script> = vec![]; let mut scripts: Vec<ScriptBuf> = vec![];
let mut rows = statement.query([])?; let mut rows = statement.query([])?;
while let Some(row) = rows.next()? { while let Some(row) = rows.next()? {
let raw_script: Vec<u8> = row.get(0)?; let raw_script: Vec<u8> = row.get(0)?;
@ -268,11 +268,11 @@ impl SqliteDatabase {
Ok(scripts) Ok(scripts)
} }
fn select_script_pubkeys_by_keychain(&self, keychain: String) -> Result<Vec<Script>, Error> { fn select_script_pubkeys_by_keychain(&self, keychain: String) -> Result<Vec<ScriptBuf>, Error> {
let mut statement = self let mut statement = self
.connection .connection
.prepare_cached("SELECT script FROM script_pubkeys WHERE keychain=:keychain")?; .prepare_cached("SELECT script FROM script_pubkeys WHERE keychain=:keychain")?;
let mut scripts: Vec<Script> = vec![]; let mut scripts: Vec<ScriptBuf> = vec![];
let mut rows = statement.query(named_params! {":keychain": keychain})?; let mut rows = statement.query(named_params! {":keychain": keychain})?;
while let Some(row) = rows.next()? { while let Some(row) = rows.next()? {
let raw_script: Vec<u8> = row.get(0)?; let raw_script: Vec<u8> = row.get(0)?;
@ -286,7 +286,7 @@ impl SqliteDatabase {
&self, &self,
keychain: String, keychain: String,
child: u32, child: u32,
) -> Result<Option<Script>, Error> { ) -> Result<Option<ScriptBuf>, Error> {
let mut statement = self.connection.prepare_cached( let mut statement = self.connection.prepare_cached(
"SELECT script FROM script_pubkeys WHERE keychain=:keychain AND child=:child", "SELECT script FROM script_pubkeys WHERE keychain=:keychain AND child=:child",
)?; )?;
@ -295,7 +295,7 @@ impl SqliteDatabase {
match rows.next()? { match rows.next()? {
Some(row) => { Some(row) => {
let script: Vec<u8> = row.get(0)?; let script: Vec<u8> = row.get(0)?;
let script: Script = script.into(); let script: ScriptBuf = script.into();
Ok(Some(script)) Ok(Some(script))
} }
None => Ok(None), None => Ok(None),
@ -362,7 +362,7 @@ impl SqliteDatabase {
let keychain: String = row.get(1)?; let keychain: String = row.get(1)?;
let keychain: KeychainKind = serde_json::from_str(&keychain)?; let keychain: KeychainKind = serde_json::from_str(&keychain)?;
let script: Vec<u8> = row.get(2)?; let script: Vec<u8> = row.get(2)?;
let script_pubkey: Script = script.into(); let script_pubkey: ScriptBuf = script.into();
let is_spent: bool = row.get(3)?; let is_spent: bool = row.get(3)?;
Ok(Some(LocalUtxo { Ok(Some(LocalUtxo {
@ -658,7 +658,7 @@ impl BatchOperations for SqliteDatabase {
utxo.txout.value, utxo.txout.value,
serde_json::to_string(&utxo.keychain)?, serde_json::to_string(&utxo.keychain)?,
utxo.outpoint.vout, utxo.outpoint.vout,
&utxo.outpoint.txid, utxo.outpoint.txid.as_ref(),
utxo.txout.script_pubkey.as_bytes(), utxo.txout.script_pubkey.as_bytes(),
utxo.is_spent, utxo.is_spent,
)?; )?;
@ -666,19 +666,19 @@ impl BatchOperations for SqliteDatabase {
} }
fn set_raw_tx(&mut self, transaction: &Transaction) -> Result<(), Error> { fn set_raw_tx(&mut self, transaction: &Transaction) -> Result<(), Error> {
match self.select_transaction_by_txid(&transaction.txid())? { match self.select_transaction_by_txid(transaction.txid().as_ref())? {
Some(_) => { Some(_) => {
self.update_transaction(&transaction.txid(), &serialize(transaction))?; self.update_transaction(transaction.txid().as_ref(), &serialize(transaction))?;
} }
None => { None => {
self.insert_transaction(&transaction.txid(), &serialize(transaction))?; self.insert_transaction(transaction.txid().as_ref(), &serialize(transaction))?;
} }
} }
Ok(()) Ok(())
} }
fn set_tx(&mut self, transaction: &TransactionDetails) -> Result<(), Error> { fn set_tx(&mut self, transaction: &TransactionDetails) -> Result<(), Error> {
match self.select_transaction_details_by_txid(&transaction.txid)? { match self.select_transaction_details_by_txid(transaction.txid.as_ref())? {
Some(_) => { Some(_) => {
self.update_transaction_details(transaction)?; self.update_transaction_details(transaction)?;
} }
@ -708,7 +708,7 @@ impl BatchOperations for SqliteDatabase {
&mut self, &mut self,
keychain: KeychainKind, keychain: KeychainKind,
child: u32, child: u32,
) -> Result<Option<Script>, Error> { ) -> Result<Option<ScriptBuf>, Error> {
let keychain = serde_json::to_string(&keychain)?; let keychain = serde_json::to_string(&keychain)?;
let script = self.select_script_pubkey_by_path(keychain.clone(), child)?; let script = self.select_script_pubkey_by_path(keychain.clone(), child)?;
match script { match script {
@ -734,9 +734,9 @@ impl BatchOperations for SqliteDatabase {
} }
fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<Option<LocalUtxo>, Error> { fn del_utxo(&mut self, outpoint: &OutPoint) -> Result<Option<LocalUtxo>, Error> {
match self.select_utxo_by_outpoint(&outpoint.txid, outpoint.vout)? { match self.select_utxo_by_outpoint(outpoint.txid.as_ref(), outpoint.vout)? {
Some(local_utxo) => { Some(local_utxo) => {
self.delete_utxo_by_outpoint(&outpoint.txid, outpoint.vout)?; self.delete_utxo_by_outpoint(outpoint.txid.as_ref(), outpoint.vout)?;
Ok(Some(local_utxo)) Ok(Some(local_utxo))
} }
None => Ok(None), None => Ok(None),
@ -744,9 +744,9 @@ impl BatchOperations for SqliteDatabase {
} }
fn del_raw_tx(&mut self, txid: &Txid) -> Result<Option<Transaction>, Error> { fn del_raw_tx(&mut self, txid: &Txid) -> Result<Option<Transaction>, Error> {
match self.select_transaction_by_txid(txid)? { match self.select_transaction_by_txid(txid.as_ref())? {
Some(tx) => { Some(tx) => {
self.delete_transaction_by_txid(txid)?; self.delete_transaction_by_txid(txid.as_ref())?;
Ok(Some(tx)) Ok(Some(tx))
} }
None => Ok(None), None => Ok(None),
@ -758,12 +758,12 @@ impl BatchOperations for SqliteDatabase {
txid: &Txid, txid: &Txid,
include_raw: bool, include_raw: bool,
) -> Result<Option<TransactionDetails>, Error> { ) -> Result<Option<TransactionDetails>, Error> {
match self.select_transaction_details_by_txid(txid)? { match self.select_transaction_details_by_txid(txid.as_ref())? {
Some(mut transaction_details) => { Some(mut transaction_details) => {
self.delete_transaction_details_by_txid(txid)?; self.delete_transaction_details_by_txid(txid.as_ref())?;
if include_raw { if include_raw {
self.delete_transaction_by_txid(txid)?; self.delete_transaction_by_txid(txid.as_ref())?;
} else { } else {
transaction_details.transaction = None; transaction_details.transaction = None;
} }
@ -820,7 +820,7 @@ impl Database for SqliteDatabase {
} }
} }
fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<Script>, Error> { fn iter_script_pubkeys(&self, keychain: Option<KeychainKind>) -> Result<Vec<ScriptBuf>, Error> {
match keychain { match keychain {
Some(keychain) => { Some(keychain) => {
let keychain = serde_json::to_string(&keychain)?; let keychain = serde_json::to_string(&keychain)?;
@ -849,7 +849,7 @@ impl Database for SqliteDatabase {
&self, &self,
keychain: KeychainKind, keychain: KeychainKind,
child: u32, child: u32,
) -> Result<Option<Script>, Error> { ) -> Result<Option<ScriptBuf>, Error> {
let keychain = serde_json::to_string(&keychain)?; let keychain = serde_json::to_string(&keychain)?;
match self.select_script_pubkey_by_path(keychain, child)? { match self.select_script_pubkey_by_path(keychain, child)? {
Some(script) => Ok(Some(script)), Some(script) => Ok(Some(script)),
@ -868,18 +868,18 @@ impl Database for SqliteDatabase {
} }
fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<LocalUtxo>, Error> { fn get_utxo(&self, outpoint: &OutPoint) -> Result<Option<LocalUtxo>, Error> {
self.select_utxo_by_outpoint(&outpoint.txid, outpoint.vout) self.select_utxo_by_outpoint(outpoint.txid.as_ref(), outpoint.vout)
} }
fn get_raw_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> { fn get_raw_tx(&self, txid: &Txid) -> Result<Option<Transaction>, Error> {
match self.select_transaction_by_txid(txid)? { match self.select_transaction_by_txid(txid.as_ref())? {
Some(tx) => Ok(Some(tx)), Some(tx) => Ok(Some(tx)),
None => Ok(None), None => Ok(None),
} }
} }
fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result<Option<TransactionDetails>, Error> { fn get_tx(&self, txid: &Txid, include_raw: bool) -> Result<Option<TransactionDetails>, Error> {
match self.select_transaction_details_by_txid(txid)? { match self.select_transaction_details_by_txid(txid.as_ref())? {
Some(mut transaction_details) => { Some(mut transaction_details) => {
if !include_raw { if !include_raw {
transaction_details.transaction = None; transaction_details.transaction = None;
@ -1115,7 +1115,7 @@ pub mod test {
let mut db = get_database(); let mut db = get_database();
let script = Script::from( let script = ScriptBuf::from(
Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(), Vec::<u8>::from_hex("76a91402306a7c23f3e8010de41e9e591348bb83f11daa88ac").unwrap(),
); );
let path = 42; let path = 42;

View File

@ -514,13 +514,14 @@ macro_rules! descriptor {
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey}; use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
$crate::impl_top_level_pk!(Pkh, $crate::miniscript::Legacy, $key) $crate::impl_top_level_pk!(Pkh, $crate::miniscript::Legacy, $key)
.and_then(|(a, b, c)| Ok((a.map_err(|e| miniscript::Error::from(e))?, b, c)))
.map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Pkh(a), b, c)) .map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Pkh(a), b, c))
}); });
( wpkh ( $key:expr ) ) => ({ ( wpkh ( $key:expr ) ) => ({
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey}; use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
$crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key) $crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key)
.and_then(|(a, b, c)| Ok((a?, b, c))) .and_then(|(a, b, c)| Ok((a.map_err(|e| miniscript::Error::from(e))?, b, c)))
.map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Wpkh(a), b, c)) .map(|(a, b, c)| (Descriptor::<DescriptorPublicKey>::Wpkh(a), b, c))
}); });
( sh ( wpkh ( $key:expr ) ) ) => ({ ( sh ( wpkh ( $key:expr ) ) ) => ({
@ -530,7 +531,7 @@ macro_rules! descriptor {
use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, Sh}; use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, Sh};
$crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key) $crate::impl_top_level_pk!(Wpkh, $crate::miniscript::Segwitv0, $key)
.and_then(|(a, b, c)| Ok((a?, b, c))) .and_then(|(a, b, c)| Ok((a.map_err(|e| miniscript::Error::from(e))?, b, c)))
.and_then(|(a, b, c)| Ok((Descriptor::<DescriptorPublicKey>::Sh(Sh::new_wpkh(a.into_inner())?), b, c))) .and_then(|(a, b, c)| Ok((Descriptor::<DescriptorPublicKey>::Sh(Sh::new_wpkh(a.into_inner())?), b, c)))
}); });
( sh ( $( $minisc:tt )* ) ) => ({ ( sh ( $( $minisc:tt )* ) ) => ({
@ -700,7 +701,7 @@ macro_rules! fragment {
$crate::keys::make_pkh($key, &secp) $crate::keys::make_pkh($key, &secp)
}); });
( after ( $value:expr ) ) => ({ ( after ( $value:expr ) ) => ({
$crate::impl_leaf_opcode_value!(After, $crate::bitcoin::PackedLockTime($value)) // TODO!! https://github.com/rust-bitcoin/rust-bitcoin/issues/1302 $crate::impl_leaf_opcode_value!(After, $crate::miniscript::AbsLockTime::from_consensus($value))
}); });
( older ( $value:expr ) ) => ({ ( older ( $value:expr ) ) => ({
$crate::impl_leaf_opcode_value!(Older, $crate::bitcoin::Sequence($value)) // TODO!! $crate::impl_leaf_opcode_value!(Older, $crate::bitcoin::Sequence($value)) // TODO!!
@ -793,7 +794,6 @@ macro_rules! fragment {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use bitcoin::hashes::hex::ToHex;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use miniscript::descriptor::{DescriptorPublicKey, KeyMap}; use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
use miniscript::{Descriptor, Legacy, Segwitv0}; use miniscript::{Descriptor, Legacy, Segwitv0};
@ -802,8 +802,8 @@ mod test {
use crate::descriptor::{DescriptorError, DescriptorMeta}; use crate::descriptor::{DescriptorError, DescriptorMeta};
use crate::keys::{DescriptorKey, IntoDescriptorKey, ValidNetworks}; use crate::keys::{DescriptorKey, IntoDescriptorKey, ValidNetworks};
use bitcoin::bip32;
use bitcoin::network::constants::Network::{Bitcoin, Regtest, Signet, Testnet}; use bitcoin::network::constants::Network::{Bitcoin, Regtest, Signet, Testnet};
use bitcoin::util::bip32;
use bitcoin::PrivateKey; use bitcoin::PrivateKey;
// test the descriptor!() macro // test the descriptor!() macro
@ -819,18 +819,15 @@ mod test {
assert_eq!(desc.is_witness(), is_witness); assert_eq!(desc.is_witness(), is_witness);
assert_eq!(!desc.has_wildcard(), is_fixed); assert_eq!(!desc.has_wildcard(), is_fixed);
for i in 0..expected.len() { for i in 0..expected.len() {
let index = i as u32; let child_desc = desc
let child_desc = if !desc.has_wildcard() { .at_derivation_index(i as u32)
desc.at_derivation_index(0) .expect("i is not hardened");
} else {
desc.at_derivation_index(index)
};
let address = child_desc.address(Regtest); let address = child_desc.address(Regtest);
if let Ok(address) = address { if let Ok(address) = address {
assert_eq!(address.to_string(), *expected.get(i).unwrap()); assert_eq!(address.to_string(), *expected.get(i).unwrap());
} else { } else {
let script = child_desc.script_pubkey(); let script = child_desc.script_pubkey();
assert_eq!(script.to_hex().as_str(), *expected.get(i).unwrap()); assert_eq!(script.to_hex_string(), *expected.get(i).unwrap());
} }
} }
} }
@ -1175,9 +1172,7 @@ mod test {
} }
#[test] #[test]
#[should_panic( #[should_panic(expected = "Miniscript(ContextError(UncompressedKeysNotAllowed))")]
expected = "Miniscript(ContextError(CompressedOnly(\"04b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a87378ec38ff91d43e8c2092ebda601780485263da089465619e0358a5c1be7ac91f4\")))"
)]
fn test_dsl_miniscript_checks() { fn test_dsl_miniscript_checks() {
let mut uncompressed_pk = let mut uncompressed_pk =
PrivateKey::from_wif("L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6").unwrap(); PrivateKey::from_wif("L5EZftvrYaSudiozVRzTqLcHLNDoVn7H5HSfM9BAN6tMJX8oTWz6").unwrap();

View File

@ -30,11 +30,11 @@ pub enum Error {
InvalidDescriptorCharacter(u8), InvalidDescriptorCharacter(u8),
/// BIP32 error /// BIP32 error
Bip32(bitcoin::util::bip32::Error), Bip32(bitcoin::bip32::Error),
/// Error during base58 decoding /// Error during base58 decoding
Base58(bitcoin::util::base58::Error), Base58(bitcoin::base58::Error),
/// Key-related error /// Key-related error
Pk(bitcoin::util::key::Error), Pk(bitcoin::key::Error),
/// Miniscript error /// Miniscript error
Miniscript(miniscript::Error), Miniscript(miniscript::Error),
/// Hex decoding error /// Hex decoding error
@ -78,9 +78,9 @@ impl std::fmt::Display for Error {
impl std::error::Error for Error {} impl std::error::Error for Error {}
impl_error!(bitcoin::util::bip32::Error, Bip32); impl_error!(bitcoin::bip32::Error, Bip32);
impl_error!(bitcoin::util::base58::Error, Base58); impl_error!(bitcoin::base58::Error, Base58);
impl_error!(bitcoin::util::key::Error, Pk); impl_error!(bitcoin::key::Error, Pk);
impl_error!(miniscript::Error, Miniscript); impl_error!(miniscript::Error, Miniscript);
impl_error!(bitcoin::hashes::hex::Error, Hex); impl_error!(bitcoin::hashes::hex::Error, Hex);
impl_error!(crate::descriptor::policy::PolicyError, Policy); impl_error!(crate::descriptor::policy::PolicyError, Policy);

View File

@ -16,17 +16,17 @@
use std::collections::BTreeMap; use std::collections::BTreeMap;
use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource}; use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource};
use bitcoin::util::{psbt, taproot}; use bitcoin::{key::XOnlyPublicKey, secp256k1, PublicKey};
use bitcoin::{secp256k1, PublicKey, XOnlyPublicKey}; use bitcoin::{psbt, taproot};
use bitcoin::{Network, TxOut}; use bitcoin::{Network, TxOut};
use miniscript::descriptor::{ use miniscript::descriptor::{
DefiniteDescriptorKey, DescriptorSecretKey, DescriptorType, InnerXKey, SinglePubKey, DefiniteDescriptorKey, DescriptorMultiXKey, DescriptorSecretKey, DescriptorType,
DescriptorXKey, InnerXKey, KeyMap, SinglePubKey, Wildcard,
}; };
pub use miniscript::{ pub use miniscript::{
descriptor::DescriptorXKey, descriptor::KeyMap, descriptor::Wildcard, Descriptor, Descriptor, DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
}; };
use miniscript::{ForEachKey, MiniscriptKey, TranslatePk}; use miniscript::{ForEachKey, MiniscriptKey, TranslatePk};
@ -57,16 +57,16 @@ pub type DerivedDescriptor = Descriptor<DefiniteDescriptorKey>;
/// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or /// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or
/// [`psbt::Output`] /// [`psbt::Output`]
/// ///
/// [`psbt::Input`]: bitcoin::util::psbt::Input /// [`psbt::Input`]: bitcoin::psbt::Input
/// [`psbt::Output`]: bitcoin::util::psbt::Output /// [`psbt::Output`]: bitcoin::psbt::Output
pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>; pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>;
/// Alias for the type of maps that represent taproot key origins in a [`psbt::Input`] or /// Alias for the type of maps that represent taproot key origins in a [`psbt::Input`] or
/// [`psbt::Output`] /// [`psbt::Output`]
/// ///
/// [`psbt::Input`]: bitcoin::util::psbt::Input /// [`psbt::Input`]: bitcoin::psbt::Input
/// [`psbt::Output`]: bitcoin::util::psbt::Output /// [`psbt::Output`]: bitcoin::psbt::Output
pub type TapKeyOrigins = BTreeMap<bitcoin::XOnlyPublicKey, (Vec<taproot::TapLeafHash>, KeySource)>; pub type TapKeyOrigins = BTreeMap<XOnlyPublicKey, (Vec<taproot::TapLeafHash>, KeySource)>;
/// Trait for types which can be converted into an [`ExtendedDescriptor`] and a [`KeyMap`] usable by a wallet in a specific [`Network`] /// Trait for types which can be converted into an [`ExtendedDescriptor`] and a [`KeyMap`] usable by a wallet in a specific [`Network`]
pub trait IntoWalletDescriptor { pub trait IntoWalletDescriptor {
@ -134,14 +134,10 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
network: Network, network: Network,
} }
impl<'s, 'd> impl<'s, 'd> miniscript::Translator<DescriptorPublicKey, String, DescriptorError>
miniscript::Translator<DescriptorPublicKey, miniscript::DummyKey, DescriptorError>
for Translator<'s, 'd> for Translator<'s, 'd>
{ {
fn pk( fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, DescriptorError> {
&mut self,
pk: &DescriptorPublicKey,
) -> Result<miniscript::DummyKey, DescriptorError> {
let secp = &self.secp; let secp = &self.secp;
let (_, _, networks) = if self.descriptor.is_taproot() { let (_, _, networks) = if self.descriptor.is_taproot() {
@ -159,7 +155,7 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
}; };
if networks.contains(&self.network) { if networks.contains(&self.network) {
Ok(miniscript::DummyKey) Ok(Default::default())
} else { } else {
Err(DescriptorError::Key(KeyError::InvalidNetwork)) Err(DescriptorError::Key(KeyError::InvalidNetwork))
} }
@ -167,35 +163,40 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
fn sha256( fn sha256(
&mut self, &mut self,
_sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256, _sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256,
) -> Result<miniscript::DummySha256Hash, DescriptorError> { ) -> Result<String, DescriptorError> {
Ok(Default::default()) Ok(Default::default())
} }
fn hash256( fn hash256(
&mut self, &mut self,
_hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256, _hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256,
) -> Result<miniscript::DummyHash256Hash, DescriptorError> { ) -> Result<String, DescriptorError> {
Ok(Default::default()) Ok(Default::default())
} }
fn ripemd160( fn ripemd160(
&mut self, &mut self,
_ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160, _ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160,
) -> Result<miniscript::DummyRipemd160Hash, DescriptorError> { ) -> Result<String, DescriptorError> {
Ok(Default::default()) Ok(Default::default())
} }
fn hash160( fn hash160(
&mut self, &mut self,
_hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160, _hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160,
) -> Result<miniscript::DummyHash160Hash, DescriptorError> { ) -> Result<String, DescriptorError> {
Ok(Default::default()) Ok(Default::default())
} }
} }
// check the network for the keys // check the network for the keys
self.0.translate_pk(&mut Translator { use miniscript::TranslateErr;
match self.0.translate_pk(&mut Translator {
secp, secp,
network, network,
descriptor: &self.0, descriptor: &self.0,
})?; }) {
Ok(_) => {}
Err(TranslateErr::TranslatorErr(e)) => return Err(e),
Err(TranslateErr::OuterError(e)) => return Err(e.into()),
}
Ok(self) Ok(self)
} }
@ -249,7 +250,12 @@ impl IntoWalletDescriptor for DescriptorTemplateOut {
} }
// fixup the network for keys that need it in the descriptor // fixup the network for keys that need it in the descriptor
let translated = desc.translate_pk(&mut Translator { network })?; use miniscript::TranslateErr;
let translated = match desc.translate_pk(&mut Translator { network }) {
Ok(descriptor) => descriptor,
Err(TranslateErr::TranslatorErr(e)) => return Err(e),
Err(TranslateErr::OuterError(e)) => return Err(e.into()),
};
// ...and in the key map // ...and in the key map
let fixed_keymap = keymap let fixed_keymap = keymap
.into_iter() .into_iter()
@ -338,6 +344,18 @@ pub(crate) trait XKeyUtils {
fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint; fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint;
} }
impl<T> XKeyUtils for DescriptorMultiXKey<T>
where
T: InnerXKey,
{
fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
match self.origin {
Some((fingerprint, _)) => fingerprint,
None => self.xkey.xkey_fingerprint(secp),
}
}
}
impl<T> XKeyUtils for DescriptorXKey<T> impl<T> XKeyUtils for DescriptorXKey<T>
where where
T: InnerXKey, T: InnerXKey,
@ -492,7 +510,10 @@ impl DescriptorMeta for ExtendedDescriptor {
false false
}); });
path_found.map(|path| self.at_derivation_index(path)) path_found.map(|path| {
self.at_derivation_index(path)
.expect("We ignore hardened wildcards")
})
} }
fn derive_from_hd_keypaths<'s>( fn derive_from_hd_keypaths<'s>(
@ -543,7 +564,7 @@ impl DescriptorMeta for ExtendedDescriptor {
return None; return None;
} }
let descriptor = self.at_derivation_index(0); let descriptor = self.at_derivation_index(0).expect("0 is not hardened");
match descriptor.desc_type() { match descriptor.desc_type() {
// TODO: add pk() here // TODO: add pk() here
DescriptorType::Pkh DescriptorType::Pkh
@ -582,11 +603,10 @@ mod test {
use std::str::FromStr; use std::str::FromStr;
use assert_matches::assert_matches; use assert_matches::assert_matches;
use bitcoin::consensus::encode::deserialize;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::{bip32, psbt}; use bitcoin::ScriptBuf;
use bitcoin::Script; use bitcoin::{bip32, psbt::Psbt};
use super::*; use super::*;
use crate::psbt::PsbtUtils; use crate::psbt::PsbtUtils;
@ -597,7 +617,7 @@ mod test {
"wpkh(02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737)", "wpkh(02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737)",
) )
.unwrap(); .unwrap();
let psbt: psbt::PartiallySignedTransaction = deserialize( let psbt = Psbt::deserialize(
&Vec::<u8>::from_hex( &Vec::<u8>::from_hex(
"70736274ff010052010000000162307be8e431fbaff807cdf9cdc3fde44d7402\ "70736274ff010052010000000162307be8e431fbaff807cdf9cdc3fde44d7402\
11bc8342c31ffd6ec11fe35bcc0100000000ffffffff01328601000000000016\ 11bc8342c31ffd6ec11fe35bcc0100000000ffffffff01328601000000000016\
@ -620,7 +640,7 @@ mod test {
"pkh([0f056943/44h/0h/0h]tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/10/*)", "pkh([0f056943/44h/0h/0h]tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/10/*)",
) )
.unwrap(); .unwrap();
let psbt: psbt::PartiallySignedTransaction = deserialize( let psbt = Psbt::deserialize(
&Vec::<u8>::from_hex( &Vec::<u8>::from_hex(
"70736274ff010053010000000145843b86be54a3cd8c9e38444e1162676c00df\ "70736274ff010053010000000145843b86be54a3cd8c9e38444e1162676c00df\
e7964122a70df491ea12fd67090100000000ffffffff01c19598000000000017\ e7964122a70df491ea12fd67090100000000ffffffff01c19598000000000017\
@ -651,7 +671,7 @@ mod test {
"wsh(and_v(v:pk(03b6633fef2397a0a9de9d7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14),older(6)))", "wsh(and_v(v:pk(03b6633fef2397a0a9de9d7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14),older(6)))",
) )
.unwrap(); .unwrap();
let psbt: psbt::PartiallySignedTransaction = deserialize( let psbt = Psbt::deserialize(
&Vec::<u8>::from_hex( &Vec::<u8>::from_hex(
"70736274ff01005302000000011c8116eea34408ab6529223c9a176606742207\ "70736274ff01005302000000011c8116eea34408ab6529223c9a176606742207\
67a1ff1d46a6e3c4a88243ea6e01000000000600000001109698000000000017\ 67a1ff1d46a6e3c4a88243ea6e01000000000600000001109698000000000017\
@ -675,7 +695,7 @@ mod test {
"sh(and_v(v:pk(021403881a5587297818fcaf17d239cefca22fce84a45b3b1d23e836c4af671dbb),after(630000)))", "sh(and_v(v:pk(021403881a5587297818fcaf17d239cefca22fce84a45b3b1d23e836c4af671dbb),after(630000)))",
) )
.unwrap(); .unwrap();
let psbt: psbt::PartiallySignedTransaction = deserialize( let psbt = Psbt::deserialize(
&Vec::<u8>::from_hex( &Vec::<u8>::from_hex(
"70736274ff0100530100000001bc8c13df445dfadcc42afa6dc841f85d22b01d\ "70736274ff0100530100000001bc8c13df445dfadcc42afa6dc841f85d22b01d\
a6270ebf981740f4b7b1d800390000000000feffffff01ba9598000000000017\ a6270ebf981740f4b7b1d800390000000000feffffff01ba9598000000000017\
@ -858,9 +878,9 @@ mod test {
let (descriptor, _) = let (descriptor, _) =
into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet).unwrap(); into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet).unwrap();
let descriptor = descriptor.at_derivation_index(0); let descriptor = descriptor.at_derivation_index(0).unwrap();
let script = Script::from_str("5321022f533b667e2ea3b36e21961c9fe9dca340fbe0af5210173a83ae0337ab20a57621026bb53a98e810bd0ee61a0ed1164ba6c024786d76554e793e202dc6ce9c78c4ea2102d5b8a7d66a41ffdb6f4c53d61994022e886b4f45001fb158b95c9164d45f8ca3210324b75eead2c1f9c60e8adeb5e7009fec7a29afcdb30d829d82d09562fe8bae8521032d34f8932200833487bd294aa219dcbe000b9f9b3d824799541430009f0fa55121037468f8ea99b6c64788398b5ad25480cad08f4b0d65be54ce3a55fd206b5ae4722103f72d3d96663b0ea99b0aeb0d7f273cab11a8de37885f1dddc8d9112adb87169357ae").unwrap(); let script = ScriptBuf::from_hex("5321022f533b667e2ea3b36e21961c9fe9dca340fbe0af5210173a83ae0337ab20a57621026bb53a98e810bd0ee61a0ed1164ba6c024786d76554e793e202dc6ce9c78c4ea2102d5b8a7d66a41ffdb6f4c53d61994022e886b4f45001fb158b95c9164d45f8ca3210324b75eead2c1f9c60e8adeb5e7009fec7a29afcdb30d829d82d09562fe8bae8521032d34f8932200833487bd294aa219dcbe000b9f9b3d824799541430009f0fa55121037468f8ea99b6c64788398b5ad25480cad08f4b0d65be54ce3a55fd206b5ae4722103f72d3d96663b0ea99b0aeb0d7f273cab11a8de37885f1dddc8d9112adb87169357ae").unwrap();
let mut psbt_input = psbt::Input::default(); let mut psbt_input = psbt::Input::default();
psbt_input psbt_input

View File

@ -43,9 +43,9 @@ use std::fmt;
use serde::ser::SerializeMap; use serde::ser::SerializeMap;
use serde::{Serialize, Serializer}; use serde::{Serialize, Serializer};
use bitcoin::bip32::Fingerprint;
use bitcoin::hashes::{hash160, ripemd160, sha256}; use bitcoin::hashes::{hash160, ripemd160, sha256};
use bitcoin::util::bip32::Fingerprint; use bitcoin::{absolute, key::XOnlyPublicKey, PublicKey, Sequence};
use bitcoin::{LockTime, PublicKey, Sequence, XOnlyPublicKey};
use miniscript::descriptor::{ use miniscript::descriptor::{
DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner, DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner,
@ -66,7 +66,7 @@ use crate::wallet::utils::{After, Older, SecpCtx};
use super::checksum::calc_checksum; use super::checksum::calc_checksum;
use super::error::Error; use super::error::Error;
use super::XKeyUtils; use super::XKeyUtils;
use bitcoin::util::psbt::{Input as PsbtInput, PartiallySignedTransaction as Psbt}; use bitcoin::psbt::{self, Psbt};
use miniscript::psbt::PsbtInputSatisfier; use miniscript::psbt::PsbtInputSatisfier;
/// A unique identifier for a key /// A unique identifier for a key
@ -93,6 +93,9 @@ impl PkOrF {
.. ..
}) => PkOrF::XOnlyPubkey(*pk), }) => PkOrF::XOnlyPubkey(*pk),
DescriptorPublicKey::XPub(xpub) => PkOrF::Fingerprint(xpub.root_fingerprint(secp)), DescriptorPublicKey::XPub(xpub) => PkOrF::Fingerprint(xpub.root_fingerprint(secp)),
DescriptorPublicKey::MultiXPub(multi) => {
PkOrF::Fingerprint(multi.root_fingerprint(secp))
}
} }
} }
} }
@ -129,7 +132,7 @@ pub enum SatisfiableItem {
/// Absolute timeclock timestamp /// Absolute timeclock timestamp
AbsoluteTimelock { AbsoluteTimelock {
/// The timelock value /// The timelock value
value: LockTime, value: absolute::LockTime,
}, },
/// Relative timelock locktime /// Relative timelock locktime
RelativeTimelock { RelativeTimelock {
@ -449,11 +452,14 @@ pub struct Condition {
pub csv: Option<Sequence>, pub csv: Option<Sequence>,
/// Optional timelock condition /// Optional timelock condition
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
pub timelock: Option<LockTime>, pub timelock: Option<absolute::LockTime>,
} }
impl Condition { impl Condition {
fn merge_nlocktime(a: LockTime, b: LockTime) -> Result<LockTime, PolicyError> { fn merge_nlocktime(
a: absolute::LockTime,
b: absolute::LockTime,
) -> Result<absolute::LockTime, PolicyError> {
if !a.is_same_unit(b) { if !a.is_same_unit(b) {
Err(PolicyError::MixedTimelockUnits) Err(PolicyError::MixedTimelockUnits)
} else if a > b { } else if a > b {
@ -746,6 +752,7 @@ fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
.. ..
}) => pk.to_pubkeyhash(SigType::Ecdsa).into(), }) => pk.to_pubkeyhash(SigType::Ecdsa).into(),
DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(), DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(),
DescriptorPublicKey::MultiXPub(xpub) => xpub.root_fingerprint(secp).into(),
} }
} }
@ -783,9 +790,9 @@ fn make_generic_signature<M: Fn() -> SatisfiableItem, F: Fn(&Psbt) -> bool>(
fn generic_sig_in_psbt< fn generic_sig_in_psbt<
// C is for "check", it's a closure we use to *check* if a psbt input contains the signature // C is for "check", it's a closure we use to *check* if a psbt input contains the signature
// for a specific key // for a specific key
C: Fn(&PsbtInput, &SinglePubKey) -> bool, C: Fn(&psbt::Input, &SinglePubKey) -> bool,
// E is for "extract", it extracts a key from the bip32 derivations found in the psbt input // E is for "extract", it extracts a key from the bip32 derivations found in the psbt input
E: Fn(&PsbtInput, Fingerprint) -> Option<SinglePubKey>, E: Fn(&psbt::Input, Fingerprint) -> Option<SinglePubKey>,
>( >(
psbt: &Psbt, psbt: &Psbt,
key: &DescriptorPublicKey, key: &DescriptorPublicKey,
@ -803,6 +810,13 @@ fn generic_sig_in_psbt<
None => false, None => false,
} }
} }
DescriptorPublicKey::MultiXPub(xpub) => {
//TODO check actual derivation matches
match extract(input, xpub.root_fingerprint(secp)) {
Some(pubkey) => check(input, &pubkey),
None => false,
}
}
}) })
} }
@ -908,12 +922,12 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
} }
Terminal::After(value) => { Terminal::After(value) => {
let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { let mut policy: Policy = SatisfiableItem::AbsoluteTimelock {
value: value.into(), value: (*value).into(),
} }
.into(); .into();
policy.contribution = Satisfaction::Complete { policy.contribution = Satisfaction::Complete {
condition: Condition { condition: Condition {
timelock: Some(value.into()), timelock: Some((*value).into()),
csv: None, csv: None,
}, },
}; };
@ -925,9 +939,9 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
{ {
let after = After::new(Some(current_height), false); let after = After::new(Some(current_height), false);
let after_sat = let after_sat =
Satisfier::<bitcoin::PublicKey>::check_after(&after, value.into()); Satisfier::<bitcoin::PublicKey>::check_after(&after, (*value).into());
let inputs_sat = psbt_inputs_sat(psbt).all(|sat| { let inputs_sat = psbt_inputs_sat(psbt).all(|sat| {
Satisfier::<bitcoin::PublicKey>::check_after(&sat, value.into()) Satisfier::<bitcoin::PublicKey>::check_after(&sat, (*value).into())
}); });
if after_sat && inputs_sat { if after_sat && inputs_sat {
policy.satisfaction = policy.contribution.clone(); policy.satisfaction = policy.contribution.clone();
@ -1152,8 +1166,8 @@ mod test {
use crate::keys::{DescriptorKey, IntoDescriptorKey}; use crate::keys::{DescriptorKey, IntoDescriptorKey};
use crate::wallet::signer::SignersContainer; use crate::wallet::signer::SignersContainer;
use assert_matches::assert_matches; use assert_matches::assert_matches;
use bitcoin::bip32;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::util::bip32;
use bitcoin::Network; use bitcoin::Network;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
@ -1572,6 +1586,7 @@ mod test {
let addr = wallet_desc let addr = wallet_desc
.at_derivation_index(0) .at_derivation_index(0)
.unwrap()
.address(Network::Testnet) .address(Network::Testnet)
.unwrap(); .unwrap();
assert_eq!( assert_eq!(
@ -1638,6 +1653,7 @@ mod test {
let addr = wallet_desc let addr = wallet_desc
.at_derivation_index(0) .at_derivation_index(0)
.unwrap()
.address(Network::Testnet) .address(Network::Testnet)
.unwrap(); .unwrap();
assert_eq!( assert_eq!(

View File

@ -14,7 +14,7 @@
//! This module contains the definition of various common script templates that are ready to be //! This module contains the definition of various common script templates that are ready to be
//! used. See the documentation of each template for an example. //! used. See the documentation of each template for an example.
use bitcoin::util::bip32; use bitcoin::bip32;
use bitcoin::Network; use bitcoin::Network;
use miniscript::{Legacy, Segwitv0, Tap}; use miniscript::{Legacy, Segwitv0, Tap};
@ -215,7 +215,7 @@ impl<K: IntoDescriptorKey<Tap>> DescriptorTemplate for P2TR<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip44; /// use bdk::template::Bip44;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; /// let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
/// let wallet = Wallet::new( /// let wallet = Wallet::new(
/// Bip44(key.clone(), KeychainKind::External), /// Bip44(key.clone(), KeychainKind::External),
/// Some(Bip44(key, KeychainKind::Internal)), /// Some(Bip44(key, KeychainKind::Internal)),
@ -254,8 +254,8 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip44Public; /// use bdk::template::Bip44Public;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?; /// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
/// let wallet = Wallet::new( /// let wallet = Wallet::new(
/// Bip44Public(key.clone(), fingerprint, KeychainKind::External), /// Bip44Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)), /// Some(Bip44Public(key, fingerprint, KeychainKind::Internal)),
@ -294,7 +294,7 @@ impl<K: DerivableKey<Legacy>> DescriptorTemplate for Bip44Public<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip49; /// use bdk::template::Bip49;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; /// let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
/// let wallet = Wallet::new( /// let wallet = Wallet::new(
/// Bip49(key.clone(), KeychainKind::External), /// Bip49(key.clone(), KeychainKind::External),
/// Some(Bip49(key, KeychainKind::Internal)), /// Some(Bip49(key, KeychainKind::Internal)),
@ -333,8 +333,8 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip49Public; /// use bdk::template::Bip49Public;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?; /// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
/// let wallet = Wallet::new( /// let wallet = Wallet::new(
/// Bip49Public(key.clone(), fingerprint, KeychainKind::External), /// Bip49Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)), /// Some(Bip49Public(key, fingerprint, KeychainKind::Internal)),
@ -373,7 +373,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip49Public<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip84; /// use bdk::template::Bip84;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; /// let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
/// let wallet = Wallet::new( /// let wallet = Wallet::new(
/// Bip84(key.clone(), KeychainKind::External), /// Bip84(key.clone(), KeychainKind::External),
/// Some(Bip84(key, KeychainKind::Internal)), /// Some(Bip84(key, KeychainKind::Internal)),
@ -412,8 +412,8 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip84Public; /// use bdk::template::Bip84Public;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?; /// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
/// let wallet = Wallet::new( /// let wallet = Wallet::new(
/// Bip84Public(key.clone(), fingerprint, KeychainKind::External), /// Bip84Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)), /// Some(Bip84Public(key, fingerprint, KeychainKind::Internal)),
@ -452,7 +452,7 @@ impl<K: DerivableKey<Segwitv0>> DescriptorTemplate for Bip84Public<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip86; /// use bdk::template::Bip86;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?; /// let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPeZRHk4rTG6orPS2CRNFX3njhUXx5vj9qGog5ZMH4uGReDWN5kCkY3jmWEtWause41CDvBRXD1shKknAMKxT99o9qUTRVC6m")?;
/// let mut wallet = Wallet::new( /// let mut wallet = Wallet::new(
/// Bip86(key.clone(), KeychainKind::External), /// Bip86(key.clone(), KeychainKind::External),
/// Some(Bip86(key, KeychainKind::Internal)), /// Some(Bip86(key, KeychainKind::Internal)),
@ -491,8 +491,8 @@ impl<K: DerivableKey<Tap>> DescriptorTemplate for Bip86<K> {
/// # use bdk::wallet::AddressIndex::New; /// # use bdk::wallet::AddressIndex::New;
/// use bdk::template::Bip86Public; /// use bdk::template::Bip86Public;
/// ///
/// let key = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?; /// let key = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q")?;
/// let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f")?; /// let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f")?;
/// let mut wallet = Wallet::new( /// let mut wallet = Wallet::new(
/// Bip86Public(key.clone(), fingerprint, KeychainKind::External), /// Bip86Public(key.clone(), fingerprint, KeychainKind::External),
/// Some(Bip86Public(key, fingerprint, KeychainKind::Internal)), /// Some(Bip86Public(key, fingerprint, KeychainKind::Internal)),
@ -599,30 +599,30 @@ mod test {
// BIP44 `pkh(key/44'/{0,1}'/0'/{0,1}/*)` // BIP44 `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
#[test] #[test]
fn test_bip44_template_cointype() { fn test_bip44_template_cointype() {
use bitcoin::util::bip32::ChildNumber::{self, Hardened}; use bitcoin::bip32::ChildNumber::{self, Hardened};
let xprvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K2fpbqApQL69a4oKdGVnVN52R82Ft7d1pSqgKmajF62acJo3aMszZb6qQ22QsVECSFxvf9uyxFUvFYQMq3QbtwtRSMjLAhMf").unwrap(); let xprvkey = bitcoin::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K2fpbqApQL69a4oKdGVnVN52R82Ft7d1pSqgKmajF62acJo3aMszZb6qQ22QsVECSFxvf9uyxFUvFYQMq3QbtwtRSMjLAhMf").unwrap();
assert_eq!(Network::Bitcoin, xprvkey.network); assert_eq!(Network::Bitcoin, xprvkey.network);
let xdesc = Bip44(xprvkey, KeychainKind::Internal) let xdesc = Bip44(xprvkey, KeychainKind::Internal)
.build(Network::Bitcoin) .build(Network::Bitcoin)
.unwrap(); .unwrap();
if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 { if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 {
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().into(); let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
let purpose = path.get(0).unwrap(); let purpose = path.get(0).unwrap();
assert_matches!(purpose, Hardened { index: 44 }); assert_matches!(purpose, Hardened { index: 44 });
let coin_type = path.get(1).unwrap(); let coin_type = path.get(1).unwrap();
assert_matches!(coin_type, Hardened { index: 0 }); assert_matches!(coin_type, Hardened { index: 0 });
} }
let tprvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let tprvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
assert_eq!(Network::Testnet, tprvkey.network); assert_eq!(Network::Testnet, tprvkey.network);
let tdesc = Bip44(tprvkey, KeychainKind::Internal) let tdesc = Bip44(tprvkey, KeychainKind::Internal)
.build(Network::Testnet) .build(Network::Testnet)
.unwrap(); .unwrap();
if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 { if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 {
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().into(); let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
let purpose = path.get(0).unwrap(); let purpose = path.get(0).unwrap();
assert_matches!(purpose, Hardened { index: 44 }); assert_matches!(purpose, Hardened { index: 44 });
let coin_type = path.get(1).unwrap(); let coin_type = path.get(1).unwrap();
@ -646,9 +646,9 @@ mod test {
for i in 0..expected.len() { for i in 0..expected.len() {
let index = i as u32; let index = i as u32;
let child_desc = if !desc.has_wildcard() { let child_desc = if !desc.has_wildcard() {
desc.at_derivation_index(0) desc.at_derivation_index(0).unwrap()
} else { } else {
desc.at_derivation_index(index) desc.at_derivation_index(index).unwrap()
}; };
let address = child_desc.address(network).unwrap(); let address = child_desc.address(network).unwrap();
assert_eq!(address.to_string(), *expected.get(i).unwrap()); assert_eq!(address.to_string(), *expected.get(i).unwrap());
@ -774,7 +774,7 @@ mod test {
// BIP44 `pkh(key/44'/0'/0'/{0,1}/*)` // BIP44 `pkh(key/44'/0'/0'/{0,1}/*)`
#[test] #[test]
fn test_bip44_template() { fn test_bip44_template() {
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
check( check(
Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin), Bip44(prvkey, KeychainKind::External).build(Network::Bitcoin),
false, false,
@ -804,8 +804,8 @@ mod test {
// BIP44 public `pkh(key/{0,1}/*)` // BIP44 public `pkh(key/{0,1}/*)`
#[test] #[test]
fn test_bip44_public_template() { fn test_bip44_public_template() {
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU").unwrap(); let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("tpubDDDzQ31JkZB7VxUr9bjvBivDdqoFLrDPyLWtLapArAi51ftfmCb2DPxwLQzX65iNcXz1DGaVvyvo6JQ6rTU73r2gqdEo8uov9QKRb7nKCSU").unwrap();
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f").unwrap(); let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
check( check(
Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin), Bip44Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
false, false,
@ -835,7 +835,7 @@ mod test {
// BIP49 `sh(wpkh(key/49'/0'/0'/{0,1}/*))` // BIP49 `sh(wpkh(key/49'/0'/0'/{0,1}/*))`
#[test] #[test]
fn test_bip49_template() { fn test_bip49_template() {
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
check( check(
Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin), Bip49(prvkey, KeychainKind::External).build(Network::Bitcoin),
true, true,
@ -865,8 +865,8 @@ mod test {
// BIP49 public `sh(wpkh(key/{0,1}/*))` // BIP49 public `sh(wpkh(key/{0,1}/*))`
#[test] #[test]
fn test_bip49_public_template() { fn test_bip49_public_template() {
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L").unwrap(); let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC49r947KGK52X5rBWS4BLs5m9SRY3pYHnvRrm7HcybZ3BfdEsGFyzCMzayi1u58eT82ZeyFZwH7DD6Q83E3fM9CpfMtmnTygnLfP59jL9L").unwrap();
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f").unwrap(); let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
check( check(
Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin), Bip49Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
true, true,
@ -896,7 +896,7 @@ mod test {
// BIP84 `wpkh(key/84'/0'/0'/{0,1}/*)` // BIP84 `wpkh(key/84'/0'/0'/{0,1}/*)`
#[test] #[test]
fn test_bip84_template() { fn test_bip84_template() {
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
check( check(
Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin), Bip84(prvkey, KeychainKind::External).build(Network::Bitcoin),
true, true,
@ -926,8 +926,8 @@ mod test {
// BIP84 public `wpkh(key/{0,1}/*)` // BIP84 public `wpkh(key/{0,1}/*)`
#[test] #[test]
fn test_bip84_public_template() { fn test_bip84_public_template() {
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q").unwrap(); let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("tpubDC2Qwo2TFsaNC4ju8nrUJ9mqVT3eSgdmy1yPqhgkjwmke3PRXutNGRYAUo6RCHTcVQaDR3ohNU9we59brGHuEKPvH1ags2nevW5opEE9Z5Q").unwrap();
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("c55b303f").unwrap(); let fingerprint = bitcoin::bip32::Fingerprint::from_str("c55b303f").unwrap();
check( check(
Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin), Bip84Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
true, true,
@ -958,7 +958,7 @@ mod test {
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki // Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
#[test] #[test]
fn test_bip86_template() { fn test_bip86_template() {
let prvkey = bitcoin::util::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap(); let prvkey = bitcoin::bip32::ExtendedPrivKey::from_str("xprv9s21ZrQH143K3GJpoapnV8SFfukcVBSfeCficPSGfubmSFDxo1kuHnLisriDvSnRRuL2Qrg5ggqHKNVpxR86QEC8w35uxmGoggxtQTPvfUu").unwrap();
check( check(
Bip86(prvkey, KeychainKind::External).build(Network::Bitcoin), Bip86(prvkey, KeychainKind::External).build(Network::Bitcoin),
false, false,
@ -989,8 +989,8 @@ mod test {
// Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki // Used addresses in test vector in https://github.com/bitcoin/bips/blob/master/bip-0086.mediawiki
#[test] #[test]
fn test_bip86_public_template() { fn test_bip86_public_template() {
let pubkey = bitcoin::util::bip32::ExtendedPubKey::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap(); let pubkey = bitcoin::bip32::ExtendedPubKey::from_str("xpub6BgBgsespWvERF3LHQu6CnqdvfEvtMcQjYrcRzx53QJjSxarj2afYWcLteoGVky7D3UKDP9QyrLprQ3VCECoY49yfdDEHGCtMMj92pReUsQ").unwrap();
let fingerprint = bitcoin::util::bip32::Fingerprint::from_str("73c5da0a").unwrap(); let fingerprint = bitcoin::bip32::Fingerprint::from_str("73c5da0a").unwrap();
check( check(
Bip86Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin), Bip86Public(pubkey, fingerprint, KeychainKind::External).build(Network::Bitcoin),
false, false,

View File

@ -86,6 +86,8 @@ pub enum Error {
/// found network, for example the network of the bitcoin node /// found network, for example the network of the bitcoin node
found: Network, found: Network,
}, },
/// The address requested comes from an hardened index
HardenedIndex,
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
/// Transaction verification error /// Transaction verification error
Verification(crate::wallet::verify::VerifyError), Verification(crate::wallet::verify::VerifyError),
@ -106,7 +108,7 @@ pub enum Error {
/// Miniscript PSBT error /// Miniscript PSBT error
MiniscriptPsbt(MiniscriptPsbtError), MiniscriptPsbt(MiniscriptPsbtError),
/// BIP32 error /// BIP32 error
Bip32(bitcoin::util::bip32::Error), Bip32(bitcoin::bip32::Error),
/// A secp256k1 error /// A secp256k1 error
Secp256k1(bitcoin::secp256k1::Error), Secp256k1(bitcoin::secp256k1::Error),
/// Error serializing or deserializing JSON data /// Error serializing or deserializing JSON data
@ -114,9 +116,9 @@ pub enum Error {
/// Hex decoding error /// Hex decoding error
Hex(bitcoin::hashes::hex::Error), Hex(bitcoin::hashes::hex::Error),
/// Partially signed bitcoin transaction error /// Partially signed bitcoin transaction error
Psbt(bitcoin::util::psbt::Error), Psbt(bitcoin::psbt::Error),
/// Partially signed bitcoin transaction parse error /// Partially signed bitcoin transaction parse error
PsbtParse(bitcoin::util::psbt::PsbtParseError), PsbtParse(bitcoin::psbt::PsbtParseError),
//KeyMismatch(bitcoin::secp256k1::PublicKey, bitcoin::secp256k1::PublicKey), //KeyMismatch(bitcoin::secp256k1::PublicKey, bitcoin::secp256k1::PublicKey),
//MissingInputUTXO(usize), //MissingInputUTXO(usize),
@ -228,6 +230,7 @@ impl fmt::Display for Error {
"Invalid network: requested {} but found {}", "Invalid network: requested {} but found {}",
requested, found requested, found
), ),
Self::HardenedIndex => write!(f, "Requested address from an hardened index"),
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
Self::Verification(err) => write!(f, "Transaction verification error: {}", err), Self::Verification(err) => write!(f, "Transaction verification error: {}", err),
Self::InvalidProgressValue(progress) => { Self::InvalidProgressValue(progress) => {
@ -304,12 +307,12 @@ impl From<crate::keys::KeyError> for Error {
impl_error!(bitcoin::consensus::encode::Error, Encode); impl_error!(bitcoin::consensus::encode::Error, Encode);
impl_error!(miniscript::Error, Miniscript); impl_error!(miniscript::Error, Miniscript);
impl_error!(MiniscriptPsbtError, MiniscriptPsbt); impl_error!(MiniscriptPsbtError, MiniscriptPsbt);
impl_error!(bitcoin::util::bip32::Error, Bip32); impl_error!(bitcoin::bip32::Error, Bip32);
impl_error!(bitcoin::secp256k1::Error, Secp256k1); impl_error!(bitcoin::secp256k1::Error, Secp256k1);
impl_error!(serde_json::Error, Json); impl_error!(serde_json::Error, Json);
impl_error!(bitcoin::hashes::hex::Error, Hex); impl_error!(bitcoin::hashes::hex::Error, Hex);
impl_error!(bitcoin::util::psbt::Error, Psbt); impl_error!(bitcoin::psbt::Error, Psbt);
impl_error!(bitcoin::util::psbt::PsbtParseError, PsbtParse); impl_error!(bitcoin::psbt::PsbtParseError, PsbtParse);
#[cfg(feature = "electrum")] #[cfg(feature = "electrum")]
impl_error!(electrum_client::Error, Electrum); impl_error!(electrum_client::Error, Electrum);

View File

@ -14,7 +14,7 @@
// TODO: maybe write our own implementation of bip39? Seems stupid to have an extra dependency for // 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. // something that should be fairly simple to re-implement.
use bitcoin::util::bip32; use bitcoin::bip32;
use bitcoin::Network; use bitcoin::Network;
use miniscript::ScriptContext; use miniscript::ScriptContext;
@ -141,7 +141,7 @@ impl<Ctx: ScriptContext> GeneratableKey<Ctx> for Mnemonic {
(word_count, language): Self::Options, (word_count, language): Self::Options,
entropy: Self::Entropy, entropy: Self::Entropy,
) -> Result<GeneratedKey<Self, Ctx>, Self::Error> { ) -> Result<GeneratedKey<Self, Ctx>, Self::Error> {
let entropy = &entropy.as_ref()[..(word_count as usize / 8)]; let entropy = &entropy[..(word_count as usize / 8)];
let mnemonic = Mnemonic::from_entropy_in(language, entropy)?; let mnemonic = Mnemonic::from_entropy_in(language, entropy)?;
Ok(GeneratedKey::new(mnemonic, any_network())) Ok(GeneratedKey::new(mnemonic, any_network()))
@ -152,7 +152,7 @@ impl<Ctx: ScriptContext> GeneratableKey<Ctx> for Mnemonic {
mod test { mod test {
use std::str::FromStr; use std::str::FromStr;
use bitcoin::util::bip32; use bitcoin::bip32;
use bip39::{Language, Mnemonic}; use bip39::{Language, Mnemonic};

View File

@ -19,8 +19,8 @@ use std::str::FromStr;
use bitcoin::secp256k1::{self, Secp256k1, Signing}; use bitcoin::secp256k1::{self, Secp256k1, Signing};
use bitcoin::util::bip32; use bitcoin::bip32;
use bitcoin::{Network, PrivateKey, PublicKey, XOnlyPublicKey}; use bitcoin::{key::XOnlyPublicKey, Network, PrivateKey, PublicKey};
use miniscript::descriptor::{Descriptor, DescriptorXKey, Wildcard}; use miniscript::descriptor::{Descriptor, DescriptorXKey, Wildcard};
pub use miniscript::descriptor::{ pub use miniscript::descriptor::{
@ -385,12 +385,12 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
/// ///
/// ``` /// ```
/// use bdk::bitcoin; /// use bdk::bitcoin;
/// use bdk::bitcoin::util::bip32; /// use bdk::bitcoin::bip32;
/// use bdk::keys::{DerivableKey, ExtendedKey, KeyError, ScriptContext}; /// use bdk::keys::{DerivableKey, ExtendedKey, KeyError, ScriptContext};
/// ///
/// struct MyCustomKeyType { /// struct MyCustomKeyType {
/// key_data: bitcoin::PrivateKey, /// key_data: bitcoin::PrivateKey,
/// chain_code: Vec<u8>, /// chain_code: [u8; 32],
/// network: bitcoin::Network, /// network: bitcoin::Network,
/// } /// }
/// ///
@ -401,7 +401,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
/// depth: 0, /// depth: 0,
/// parent_fingerprint: bip32::Fingerprint::default(), /// parent_fingerprint: bip32::Fingerprint::default(),
/// private_key: self.key_data.inner, /// private_key: self.key_data.inner,
/// chain_code: bip32::ChainCode::from(self.chain_code.as_ref()), /// chain_code: bip32::ChainCode::from(&self.chain_code),
/// child_number: bip32::ChildNumber::Normal { index: 0 }, /// child_number: bip32::ChildNumber::Normal { index: 0 },
/// }; /// };
/// ///
@ -416,14 +416,14 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
/// ///
/// ``` /// ```
/// use bdk::bitcoin; /// use bdk::bitcoin;
/// use bdk::bitcoin::util::bip32; /// use bdk::bitcoin::bip32;
/// use bdk::keys::{ /// use bdk::keys::{
/// any_network, DerivableKey, DescriptorKey, ExtendedKey, KeyError, ScriptContext, /// any_network, DerivableKey, DescriptorKey, ExtendedKey, KeyError, ScriptContext,
/// }; /// };
/// ///
/// struct MyCustomKeyType { /// struct MyCustomKeyType {
/// key_data: bitcoin::PrivateKey, /// key_data: bitcoin::PrivateKey,
/// chain_code: Vec<u8>, /// chain_code: [u8; 32],
/// } /// }
/// ///
/// impl<Ctx: ScriptContext> DerivableKey<Ctx> for MyCustomKeyType { /// impl<Ctx: ScriptContext> DerivableKey<Ctx> for MyCustomKeyType {
@ -433,7 +433,7 @@ impl<Ctx: ScriptContext> From<bip32::ExtendedPrivKey> for ExtendedKey<Ctx> {
/// depth: 0, /// depth: 0,
/// parent_fingerprint: bip32::Fingerprint::default(), /// parent_fingerprint: bip32::Fingerprint::default(),
/// private_key: self.key_data.inner, /// private_key: self.key_data.inner,
/// chain_code: bip32::ChainCode::from(self.chain_code.as_ref()), /// chain_code: bip32::ChainCode::from(&self.chain_code),
/// child_number: bip32::ChildNumber::Normal { index: 0 }, /// child_number: bip32::ChildNumber::Normal { index: 0 },
/// }; /// };
/// ///
@ -925,13 +925,13 @@ pub enum KeyError {
Message(String), Message(String),
/// BIP32 error /// BIP32 error
Bip32(bitcoin::util::bip32::Error), Bip32(bitcoin::bip32::Error),
/// Miniscript error /// Miniscript error
Miniscript(miniscript::Error), Miniscript(miniscript::Error),
} }
impl_error!(miniscript::Error, Miniscript, KeyError); impl_error!(miniscript::Error, Miniscript, KeyError);
impl_error!(bitcoin::util::bip32::Error, Bip32, KeyError); impl_error!(bitcoin::bip32::Error, Bip32, KeyError);
impl std::fmt::Display for KeyError { impl std::fmt::Display for KeyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -950,7 +950,7 @@ impl std::error::Error for KeyError {}
#[cfg(test)] #[cfg(test)]
pub mod test { pub mod test {
use bitcoin::util::bip32; use bitcoin::bip32;
use super::*; use super::*;

View File

@ -149,7 +149,7 @@ fn main() -> Result<(), bdk::Error> {
//! ```no_run //! ```no_run
//! use std::str::FromStr; //! use std::str::FromStr;
//! //!
//! use bitcoin::util::psbt::PartiallySignedTransaction as Psbt; //! use bitcoin::psbt::PartiallySignedTransaction as Psbt;
//! //!
//! use bdk::{Wallet, SignOptions}; //! use bdk::{Wallet, SignOptions};
//! use bdk::database::MemoryDatabase; //! use bdk::database::MemoryDatabase;

View File

@ -12,7 +12,7 @@
//! Additional functions on the `rust-bitcoin` `PartiallySignedTransaction` structure. //! Additional functions on the `rust-bitcoin` `PartiallySignedTransaction` structure.
use crate::FeeRate; use crate::FeeRate;
use bitcoin::util::psbt::PartiallySignedTransaction as Psbt; use bitcoin::psbt::PartiallySignedTransaction as Psbt;
use bitcoin::TxOut; use bitcoin::TxOut;
// TODO upstream the functions here to `rust-bitcoin`? // TODO upstream the functions here to `rust-bitcoin`?

View File

@ -10,13 +10,13 @@
use crate::testutils::TestIncomingTx; use crate::testutils::TestIncomingTx;
use bitcoin::consensus::encode::{deserialize, serialize}; use bitcoin::consensus::encode::{deserialize, serialize};
use bitcoin::hashes::hex::{FromHex, ToHex};
use bitcoin::hashes::sha256d; use bitcoin::hashes::sha256d;
use bitcoin::{Address, Amount, PackedLockTime, Script, Sequence, Transaction, Txid, Witness}; use bitcoin::{absolute, Address, Amount, Script, ScriptBuf, Sequence, Transaction, Txid, Witness};
pub use bitcoincore_rpc::bitcoincore_rpc_json::AddressType; pub use bitcoincore_rpc::json::AddressType;
pub use bitcoincore_rpc::{Auth, Client as RpcClient, RpcApi}; pub use bitcoincore_rpc::{Auth, Client as RpcClient, RpcApi};
use core::str::FromStr; use core::str::FromStr;
use electrsd::bitcoind::BitcoinD; use electrsd::bitcoind::BitcoinD;
use electrsd::electrum_client::ElectrumApi as _;
use electrsd::{bitcoind, ElectrsD}; use electrsd::{bitcoind, ElectrsD};
pub use electrum_client::{Client as ElectrumClient, ElectrumApi}; pub use electrum_client::{Client as ElectrumClient, ElectrumApi};
#[allow(unused_imports)] #[allow(unused_imports)]
@ -45,7 +45,11 @@ impl TestClient {
let electrsd = ElectrsD::with_conf(electrs_exe, &bitcoind, &conf).unwrap(); let electrsd = ElectrsD::with_conf(electrs_exe, &bitcoind, &conf).unwrap();
let node_address = bitcoind.client.get_new_address(None, None).unwrap(); let node_address = bitcoind
.client
.get_new_address(None, None)
.unwrap()
.assume_checked();
bitcoind bitcoind
.client .client
.generate_to_address(101, &node_address) .generate_to_address(101, &node_address)
@ -107,7 +111,7 @@ impl TestClient {
.collect(); .collect();
if self.get_balance(None, None).unwrap() < Amount::from_sat(required_balance) { if self.get_balance(None, None).unwrap() < Amount::from_sat(required_balance) {
panic!("Insufficient funds in bitcoind. Please generate a few blocks with: `bitcoin-cli generatetoaddress 10 {}`", self.get_new_address(None, None).unwrap()); panic!("Insufficient funds in bitcoind. Please generate a few blocks with: `bitcoin-cli generatetoaddress 10 {}`", self.get_new_address(None, None).unwrap().assume_checked());
} }
// FIXME: core can't create a tx with two outputs to the same address // FIXME: core can't create a tx with two outputs to the same address
@ -143,6 +147,7 @@ impl TestClient {
let monitor_script = Address::from_str(&meta_tx.output[0].to_address) let monitor_script = Address::from_str(&meta_tx.output[0].to_address)
.unwrap() .unwrap()
.assume_checked()
.script_pubkey(); .script_pubkey();
self.wait_for_tx(txid, &monitor_script); self.wait_for_tx(txid, &monitor_script);
@ -161,7 +166,7 @@ impl TestClient {
let bumped: serde_json::Value = self.call("bumpfee", &[txid.to_string().into()]).unwrap(); let bumped: serde_json::Value = self.call("bumpfee", &[txid.to_string().into()]).unwrap();
let new_txid = Txid::from_str(&bumped["txid"].as_str().unwrap().to_string()).unwrap(); let new_txid = Txid::from_str(&bumped["txid"].as_str().unwrap().to_string()).unwrap();
let monitor_script = Script::from_hex(&mut tx.vout[0].script_pub_key.hex.to_hex()).unwrap(); let monitor_script = ScriptBuf::from_bytes(tx.vout[0].script_pub_key.hex.clone());
self.wait_for_tx(new_txid, &monitor_script); self.wait_for_tx(new_txid, &monitor_script);
debug!("Bumped {}, new txid {}", txid, new_txid); debug!("Bumped {}, new txid {}", txid, new_txid);
@ -170,41 +175,44 @@ impl TestClient {
} }
pub fn generate_manually(&mut self, txs: Vec<Transaction>) -> String { pub fn generate_manually(&mut self, txs: Vec<Transaction>) -> String {
use bitcoin::blockdata::block::{Block, BlockHeader}; use bitcoin::blockdata::block::{Block, Header, Version};
use bitcoin::blockdata::script::Builder; use bitcoin::blockdata::script::Builder;
use bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut}; use bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut};
use bitcoin::hash_types::{BlockHash, TxMerkleNode}; use bitcoin::hash_types::{BlockHash, TxMerkleNode};
use bitcoin::hashes::Hash; use bitcoin::hashes::Hash;
use bitcoin::pow::CompactTarget;
let block_template: serde_json::Value = self let block_template: serde_json::Value = self
.call("getblocktemplate", &[json!({"rules": ["segwit"]})]) .call("getblocktemplate", &[json!({"rules": ["segwit"]})])
.unwrap(); .unwrap();
trace!("getblocktemplate: {:#?}", block_template); trace!("getblocktemplate: {:#?}", block_template);
let header = BlockHeader { let header = Header {
version: block_template["version"].as_i64().unwrap() as i32, version: Version::from_consensus(block_template["version"].as_i64().unwrap() as i32),
prev_blockhash: BlockHash::from_hex( prev_blockhash: BlockHash::from_str(
block_template["previousblockhash"].as_str().unwrap(), block_template["previousblockhash"].as_str().unwrap(),
) )
.unwrap(), .unwrap(),
merkle_root: TxMerkleNode::all_zeros(), merkle_root: TxMerkleNode::all_zeros(),
time: block_template["curtime"].as_u64().unwrap() as u32, time: block_template["curtime"].as_u64().unwrap() as u32,
bits: u32::from_str_radix(block_template["bits"].as_str().unwrap(), 16).unwrap(), bits: CompactTarget::from_consensus(
u32::from_str_radix(block_template["bits"].as_str().unwrap(), 16).unwrap(),
),
nonce: 0, nonce: 0,
}; };
debug!("header: {:#?}", header); debug!("header: {:#?}", header);
let height = block_template["height"].as_u64().unwrap() as i64; let height = block_template["height"].as_u64().unwrap() as i64;
let witness_reserved_value: Vec<u8> = sha256d::Hash::all_zeros().as_ref().into(); let witness_reserved_value = sha256d::Hash::all_zeros().as_byte_array().to_vec();
// burn block subsidy and fees, not a big deal // burn block subsidy and fees, not a big deal
let mut coinbase_tx = Transaction { let mut coinbase_tx = Transaction {
version: 1, version: 1,
lock_time: PackedLockTime(0), lock_time: absolute::LockTime::ZERO,
input: vec![TxIn { input: vec![TxIn {
previous_output: OutPoint::null(), previous_output: OutPoint::null(),
script_sig: Builder::new().push_int(height).into_script(), script_sig: Builder::new().push_int(height).into_script(),
sequence: Sequence(0xFFFFFFFF), sequence: Sequence(0xFFFFFFFF),
witness: Witness::from_vec(vec![witness_reserved_value]), witness: Witness::from_slice(&vec![witness_reserved_value]),
}], }],
output: vec![], output: vec![],
}; };
@ -225,7 +233,7 @@ impl TestClient {
// now update and replace the coinbase tx // now update and replace the coinbase tx
let mut coinbase_witness_commitment_script = vec![0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed]; let mut coinbase_witness_commitment_script = vec![0x6a, 0x24, 0xaa, 0x21, 0xa9, 0xed];
coinbase_witness_commitment_script.extend_from_slice(&witness_commitment); coinbase_witness_commitment_script.extend_from_slice(witness_commitment.as_ref());
coinbase_tx.output.push(TxOut { coinbase_tx.output.push(TxOut {
value: 0, value: 0,
@ -245,11 +253,11 @@ impl TestClient {
// now do PoW :) // now do PoW :)
let target = block.header.target(); let target = block.header.target();
while block.header.validate_pow(&target).is_err() { while block.header.validate_pow(target).is_err() {
block.header.nonce = block.header.nonce.checked_add(1).unwrap(); // panic if we run out of nonces block.header.nonce = block.header.nonce.checked_add(1).unwrap(); // panic if we run out of nonces
} }
let block_hex: String = serialize(&block).to_hex(); let block_hex: String = bitcoin::consensus::encode::serialize_hex(&block);
debug!("generated block hex: {}", block_hex); debug!("generated block hex: {}", block_hex);
self.electrsd.client.block_headers_subscribe().unwrap(); self.electrsd.client.block_headers_subscribe().unwrap();
@ -265,11 +273,12 @@ impl TestClient {
self.wait_for_block(height as usize); self.wait_for_block(height as usize);
block.header.block_hash().to_hex() block.header.block_hash().to_string()
} }
pub fn generate(&mut self, num_blocks: u64, address: Option<Address>) { pub fn generate(&mut self, num_blocks: u64, address: Option<Address>) {
let address = address.unwrap_or_else(|| self.get_new_address(None, None).unwrap()); let address =
address.unwrap_or_else(|| self.get_new_address(None, None).unwrap().assume_checked());
let hashes = self.generate_to_address(num_blocks, &address).unwrap(); let hashes = self.generate_to_address(num_blocks, &address).unwrap();
let best_hash = hashes.last().unwrap(); let best_hash = hashes.last().unwrap();
let height = self.get_block_info(best_hash).unwrap().height; let height = self.get_block_info(best_hash).unwrap().height;
@ -317,9 +326,11 @@ impl TestClient {
&self &self
.get_new_address(None, address_type) .get_new_address(None, address_type)
.unwrap() .unwrap()
.assume_checked()
.to_string(), .to_string(),
) )
.unwrap() .unwrap()
.assume_checked()
} }
} }
@ -378,7 +389,7 @@ macro_rules! bdk_blockchain_tests {
fn $_fn_name:ident ( $( $test_client:ident : &TestClient )? $(,)? ) -> $blockchain:ty $block:block) => { fn $_fn_name:ident ( $( $test_client:ident : &TestClient )? $(,)? ) -> $blockchain:ty $block:block) => {
#[cfg(test)] #[cfg(test)]
mod bdk_blockchain_tests { mod bdk_blockchain_tests {
use $crate::bitcoin::{Transaction, Network}; use $crate::bitcoin::{Transaction, Network, blockdata::script::PushBytesBuf};
use $crate::testutils::blockchain_tests::TestClient; use $crate::testutils::blockchain_tests::TestClient;
use $crate::blockchain::Blockchain; use $crate::blockchain::Blockchain;
use $crate::database::MemoryDatabase; use $crate::database::MemoryDatabase;
@ -386,6 +397,7 @@ macro_rules! bdk_blockchain_tests {
use $crate::wallet::AddressIndex; use $crate::wallet::AddressIndex;
use $crate::{Wallet, FeeRate, SyncOptions}; use $crate::{Wallet, FeeRate, SyncOptions};
use $crate::testutils; use $crate::testutils;
use std::convert::TryFrom;
use super::*; use super::*;
@ -1058,7 +1070,7 @@ macro_rules! bdk_blockchain_tests {
assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance"); assert_eq!(wallet.get_balance().unwrap().untrusted_pending, 50_000, "incorrect balance");
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
let data = [42u8;80]; let data = PushBytesBuf::try_from(vec![42u8;80]).unwrap();
builder.add_data(&data); builder.add_data(&data);
let (mut psbt, details) = builder.finish().unwrap(); let (mut psbt, details) = builder.finish().unwrap();
@ -1066,7 +1078,7 @@ macro_rules! bdk_blockchain_tests {
assert!(finalized, "Cannot finalize transaction"); assert!(finalized, "Cannot finalize transaction");
let tx = psbt.extract_tx(); let tx = psbt.extract_tx();
let serialized_tx = bitcoin::consensus::encode::serialize(&tx); let serialized_tx = bitcoin::consensus::encode::serialize(&tx);
assert!(serialized_tx.windows(data.len()).any(|e| e==data), "cannot find op_return data in transaction"); assert!(serialized_tx.windows(data.len()).any(|e| e==data.as_bytes()), "cannot find op_return data in transaction");
blockchain.broadcast(&tx).unwrap(); blockchain.broadcast(&tx).unwrap();
test_client.generate(1, Some(node_addr)); test_client.generate(1, Some(node_addr));
wallet.sync(&blockchain, SyncOptions::default()).unwrap(); wallet.sync(&blockchain, SyncOptions::default()).unwrap();
@ -1165,8 +1177,9 @@ macro_rules! bdk_blockchain_tests {
// 2. Get a new bech32m address from test bitcoind node taproot wallet // 2. Get a new bech32m address from test bitcoind node taproot wallet
// TODO replace once rust-bitcoincore-rpc with PR 199 released // TODO replace once rust-bitcoincore-rpc with PR 199 released
let node_addr: bitcoin::Address = taproot_wallet_client.call("getnewaddress", &["test address".into(), "bech32m".into()]).unwrap(); let node_addr: bitcoin::Address<bitcoin::address::NetworkUnchecked> = taproot_wallet_client.call("getnewaddress", &["test address".into(), "bech32m".into()]).unwrap();
assert_eq!(node_addr, bitcoin::Address::from_str("bcrt1pj5y3f0fu4y7g98k4v63j9n0xvj3lmln0cpwhsjzknm6nt0hr0q7qnzwsy9").unwrap()); let node_addr = node_addr.assume_checked();
assert_eq!(node_addr, bitcoin::Address::from_str("bcrt1pj5y3f0fu4y7g98k4v63j9n0xvj3lmln0cpwhsjzknm6nt0hr0q7qnzwsy9").unwrap().assume_checked());
// 3. Send 50_000 sats from test bitcoind node to test BDK wallet // 3. Send 50_000 sats from test bitcoind node to test BDK wallet
@ -1215,7 +1228,7 @@ macro_rules! bdk_blockchain_tests {
let (wallet, blockchain, _, mut test_client) = init_single_sig(); let (wallet, blockchain, _, mut test_client) = init_single_sig();
let bdk_address = wallet.get_address(AddressIndex::New).unwrap().address; let bdk_address = wallet.get_address(AddressIndex::New).unwrap().address;
let core_address = test_client.get_new_address(None, None).unwrap(); let core_address = test_client.get_new_address(None, None).unwrap().assume_checked();
let tx = testutils! { let tx = testutils! {
@tx ( (@addr bdk_address.clone()) => 50_000, (@addr core_address.clone()) => 40_000 ) @tx ( (@addr bdk_address.clone()) => 50_000, (@addr core_address.clone()) => 40_000 )
}; };
@ -1407,8 +1420,8 @@ macro_rules! bdk_blockchain_tests {
"label":"taproot key spend", "label":"taproot key spend",
}]); }]);
let _importdescriptors_result: Value = taproot_wallet_client.call("importdescriptors", &[import_descriptor_args]).expect("import wallet"); let _importdescriptors_result: Value = taproot_wallet_client.call("importdescriptors", &[import_descriptor_args]).expect("import wallet");
let generate_to_address: bitcoin::Address = taproot_wallet_client.call("getnewaddress", &["test address".into(), "bech32m".into()]).expect("new address"); let generate_to_address: bitcoin::Address<bitcoin::address::NetworkUnchecked> = taproot_wallet_client.call("getnewaddress", &["test address".into(), "bech32m".into()]).expect("new address");
let _generatetoaddress_result = taproot_wallet_client.generate_to_address(101, &generate_to_address).expect("generated to address"); let _generatetoaddress_result = taproot_wallet_client.generate_to_address(101, &generate_to_address.assume_checked()).expect("generated to address");
let send_to_address = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address.to_string(); let send_to_address = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address.to_string();
let change_address = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address.to_string(); let change_address = wallet.get_address($crate::wallet::AddressIndex::New).unwrap().address.to_string();
let send_addr_amounts = json!([{ let send_addr_amounts = json!([{
@ -1421,7 +1434,7 @@ macro_rules! bdk_blockchain_tests {
let send_result: Value = taproot_wallet_client.call("send", &[send_addr_amounts, Value::Null, "unset".into(), Value::Null, send_options]).expect("send psbt"); let send_result: Value = taproot_wallet_client.call("send", &[send_addr_amounts, Value::Null, "unset".into(), Value::Null, send_options]).expect("send psbt");
let core_psbt = send_result["psbt"].as_str().expect("core psbt str"); let core_psbt = send_result["psbt"].as_str().expect("core psbt str");
use bitcoin::util::psbt::PartiallySignedTransaction; use bitcoin::psbt::PartiallySignedTransaction;
// Test parsing core created PSBT // Test parsing core created PSBT
let mut psbt = PartiallySignedTransaction::from_str(&core_psbt).expect("core taproot psbt"); let mut psbt = PartiallySignedTransaction::from_str(&core_psbt).expect("core taproot psbt");

View File

@ -106,7 +106,7 @@ macro_rules! testutils {
let secp = Secp256k1::new(); let secp = Secp256k1::new();
let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &$descriptors.0).expect("Failed to parse descriptor in `testutils!(@external)`").0; let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &$descriptors.0).expect("Failed to parse descriptor in `testutils!(@external)`").0;
parsed.at_derivation_index($child).address(bitcoin::Network::Regtest).expect("No address form") parsed.at_derivation_index($child).unwrap().address(bitcoin::Network::Regtest).expect("No address form")
}); });
( @internal $descriptors:expr, $child:expr ) => ({ ( @internal $descriptors:expr, $child:expr ) => ({
use $crate::bitcoin::secp256k1::Secp256k1; use $crate::bitcoin::secp256k1::Secp256k1;
@ -146,7 +146,7 @@ macro_rules! testutils {
let mut seed = [0u8; 32]; let mut seed = [0u8; 32];
rand::thread_rng().fill(&mut seed[..]); rand::thread_rng().fill(&mut seed[..]);
let key = $crate::bitcoin::util::bip32::ExtendedPrivKey::new_master( let key = $crate::bitcoin::bip32::ExtendedPrivKey::new_master(
$crate::bitcoin::Network::Testnet, $crate::bitcoin::Network::Testnet,
&seed, &seed,
); );

View File

@ -13,7 +13,7 @@ use std::convert::AsRef;
use std::ops::Sub; use std::ops::Sub;
use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut}; use bitcoin::blockdata::transaction::{OutPoint, Transaction, TxOut};
use bitcoin::{hash_types::Txid, util::psbt}; use bitcoin::{hash_types::Txid, psbt, Weight};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -97,8 +97,8 @@ impl FeeRate {
} }
/// Calculate fee rate from `fee` and weight units (`wu`). /// Calculate fee rate from `fee` and weight units (`wu`).
pub fn from_wu(fee: u64, wu: usize) -> FeeRate { pub fn from_wu(fee: u64, wu: Weight) -> FeeRate {
Self::from_vb(fee, wu.vbytes()) Self::from_vb(fee, wu.to_vbytes_ceil() as usize)
} }
/// Calculate fee rate from `fee` and `vbytes`. /// Calculate fee rate from `fee` and `vbytes`.
@ -113,8 +113,8 @@ impl FeeRate {
} }
/// Calculate absolute fee in Satoshis using size in weight units. /// Calculate absolute fee in Satoshis using size in weight units.
pub fn fee_wu(&self, wu: usize) -> u64 { pub fn fee_wu(&self, wu: Weight) -> u64 {
self.fee_vb(wu.vbytes()) self.fee_vb(wu.to_vbytes_ceil() as usize)
} }
/// Calculate absolute fee in Satoshis using size in virtual bytes. /// Calculate absolute fee in Satoshis using size in virtual bytes.
@ -405,7 +405,7 @@ mod tests {
let tx_details_a = TransactionDetails { let tx_details_a = TransactionDetails {
transaction: None, transaction: None,
txid: Txid::from_inner([0; 32]), txid: Txid::all_zeros(),
received: 0, received: 0,
sent: 0, sent: 0,
fee: None, fee: None,
@ -414,7 +414,7 @@ mod tests {
let tx_details_b = TransactionDetails { let tx_details_b = TransactionDetails {
transaction: None, transaction: None,
txid: Txid::from_inner([0; 32]), txid: Txid::all_zeros(),
received: 0, received: 0,
sent: 0, sent: 0,
fee: None, fee: None,
@ -423,7 +423,7 @@ mod tests {
let tx_details_c = TransactionDetails { let tx_details_c = TransactionDetails {
transaction: None, transaction: None,
txid: Txid::from_inner([0; 32]), txid: Txid::all_zeros(),
received: 0, received: 0,
sent: 0, sent: 0,
fee: None, fee: None,
@ -432,7 +432,7 @@ mod tests {
let tx_details_d = TransactionDetails { let tx_details_d = TransactionDetails {
transaction: None, transaction: None,
txid: Txid::from_inner([1; 32]), txid: Txid::from_byte_array([1; Txid::LEN]),
received: 0, received: 0,
sent: 0, sent: 0,
fee: None, fee: None,

View File

@ -40,12 +40,12 @@
//! database: &D, //! database: &D,
//! required_utxos: Vec<WeightedUtxo>, //! required_utxos: Vec<WeightedUtxo>,
//! optional_utxos: Vec<WeightedUtxo>, //! optional_utxos: Vec<WeightedUtxo>,
//! fee_rate: FeeRate, //! fee_rate: bdk::FeeRate,
//! target_amount: u64, //! target_amount: u64,
//! drain_script: &Script, //! drain_script: &Script,
//! ) -> Result<CoinSelectionResult, bdk::Error> { //! ) -> Result<CoinSelectionResult, bdk::Error> {
//! let mut selected_amount = 0; //! let mut selected_amount = 0;
//! let mut additional_weight = 0; //! let mut additional_weight = Weight::ZERO;
//! let all_utxos_selected = required_utxos //! let all_utxos_selected = required_utxos
//! .into_iter() //! .into_iter()
//! .chain(optional_utxos) //! .chain(optional_utxos)
@ -53,7 +53,9 @@
//! (&mut selected_amount, &mut additional_weight), //! (&mut selected_amount, &mut additional_weight),
//! |(selected_amount, additional_weight), weighted_utxo| { //! |(selected_amount, additional_weight), weighted_utxo| {
//! **selected_amount += weighted_utxo.utxo.txout().value; //! **selected_amount += weighted_utxo.utxo.txout().value;
//! **additional_weight += TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight; //! **additional_weight += Weight::from_wu(
//! (TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
//! );
//! Some(weighted_utxo.utxo) //! Some(weighted_utxo.utxo)
//! }, //! },
//! ) //! )
@ -82,7 +84,10 @@
//! # let wallet = doctest_wallet!(); //! # let wallet = doctest_wallet!();
//! // create wallet, sync, ... //! // create wallet, sync, ...
//! //!
//! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); //! let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
//! .unwrap()
//! .require_network(Network::Testnet)
//! .unwrap();
//! let (psbt, details) = { //! let (psbt, details) = {
//! let mut builder = wallet.build_tx().coin_selection(AlwaysSpendEverything); //! let mut builder = wallet.build_tx().coin_selection(AlwaysSpendEverything);
//! builder.add_recipient(to_address.script_pubkey(), 50_000); //! builder.add_recipient(to_address.script_pubkey(), 50_000);
@ -100,7 +105,7 @@ use crate::{database::Database, WeightedUtxo};
use crate::{error::Error, Utxo}; use crate::{error::Error, Utxo};
use bitcoin::consensus::encode::serialize; use bitcoin::consensus::encode::serialize;
use bitcoin::Script; use bitcoin::{Script, Weight};
#[cfg(test)] #[cfg(test)]
use assert_matches::assert_matches; use assert_matches::assert_matches;
@ -337,8 +342,9 @@ fn select_sorted_utxos(
(&mut selected_amount, &mut fee_amount), (&mut selected_amount, &mut fee_amount),
|(selected_amount, fee_amount), (must_use, weighted_utxo)| { |(selected_amount, fee_amount), (must_use, weighted_utxo)| {
if must_use || **selected_amount < target_amount + **fee_amount { if must_use || **selected_amount < target_amount + **fee_amount {
**fee_amount += **fee_amount += fee_rate.fee_wu(Weight::from_wu(
fee_rate.fee_wu(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight); (TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
));
**selected_amount += weighted_utxo.utxo.txout().value; **selected_amount += weighted_utxo.utxo.txout().value;
log::debug!( log::debug!(
@ -386,7 +392,9 @@ struct OutputGroup {
impl OutputGroup { impl OutputGroup {
fn new(weighted_utxo: WeightedUtxo, fee_rate: FeeRate) -> Self { fn new(weighted_utxo: WeightedUtxo, fee_rate: FeeRate) -> Self {
let fee = fee_rate.fee_wu(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight); let fee = fee_rate.fee_wu(Weight::from_wu(
(TXIN_BASE_WEIGHT + weighted_utxo.satisfaction_weight) as u64,
));
let effective_value = weighted_utxo.utxo.txout().value as i64 - fee as i64; let effective_value = weighted_utxo.utxo.txout().value as i64 - fee as i64;
OutputGroup { OutputGroup {
weighted_utxo, weighted_utxo,
@ -724,7 +732,7 @@ impl BranchAndBoundCoinSelection {
mod test { mod test {
use std::str::FromStr; use std::str::FromStr;
use bitcoin::{OutPoint, Script, TxOut}; use bitcoin::{OutPoint, ScriptBuf, TxOut};
use super::*; use super::*;
use crate::database::{BatchOperations, MemoryDatabase}; use crate::database::{BatchOperations, MemoryDatabase};
@ -754,7 +762,7 @@ mod test {
outpoint, outpoint,
txout: TxOut { txout: TxOut {
value, value,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
keychain: KeychainKind::External, keychain: KeychainKind::External,
is_spent: false, is_spent: false,
@ -837,7 +845,7 @@ mod test {
.unwrap(), .unwrap(),
txout: TxOut { txout: TxOut {
value: rng.gen_range(0..200000000), value: rng.gen_range(0..200000000),
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
keychain: KeychainKind::External, keychain: KeychainKind::External,
is_spent: false, is_spent: false,
@ -857,7 +865,7 @@ mod test {
.unwrap(), .unwrap(),
txout: TxOut { txout: TxOut {
value: utxos_value, value: utxos_value,
script_pubkey: Script::new(), script_pubkey: ScriptBuf::new(),
}, },
keychain: KeychainKind::External, keychain: KeychainKind::External,
is_spent: false, is_spent: false,
@ -879,7 +887,7 @@ mod test {
fn test_largest_first_coin_selection_success() { fn test_largest_first_coin_selection_success() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 250_000 + FEE_AMOUNT; let target_amount = 250_000 + FEE_AMOUNT;
let result = LargestFirstCoinSelection::default() let result = LargestFirstCoinSelection::default()
@ -902,7 +910,7 @@ mod test {
fn test_largest_first_coin_selection_use_all() { fn test_largest_first_coin_selection_use_all() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = LargestFirstCoinSelection::default() let result = LargestFirstCoinSelection::default()
@ -925,7 +933,7 @@ mod test {
fn test_largest_first_coin_selection_use_only_necessary() { fn test_largest_first_coin_selection_use_only_necessary() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = LargestFirstCoinSelection::default() let result = LargestFirstCoinSelection::default()
@ -949,7 +957,7 @@ mod test {
fn test_largest_first_coin_selection_insufficient_funds() { fn test_largest_first_coin_selection_insufficient_funds() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 500_000 + FEE_AMOUNT; let target_amount = 500_000 + FEE_AMOUNT;
LargestFirstCoinSelection::default() LargestFirstCoinSelection::default()
@ -969,7 +977,7 @@ mod test {
fn test_largest_first_coin_selection_insufficient_funds_high_fees() { fn test_largest_first_coin_selection_insufficient_funds_high_fees() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 250_000 + FEE_AMOUNT; let target_amount = 250_000 + FEE_AMOUNT;
LargestFirstCoinSelection::default() LargestFirstCoinSelection::default()
@ -988,7 +996,7 @@ mod test {
fn test_oldest_first_coin_selection_success() { fn test_oldest_first_coin_selection_success() {
let mut database = MemoryDatabase::default(); let mut database = MemoryDatabase::default();
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database); let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 180_000 + FEE_AMOUNT; let target_amount = 180_000 + FEE_AMOUNT;
let result = OldestFirstCoinSelection::default() let result = OldestFirstCoinSelection::default()
@ -1013,7 +1021,7 @@ mod test {
let utxo1 = utxo(120_000, 1); let utxo1 = utxo(120_000, 1);
let utxo2 = utxo(80_000, 2); let utxo2 = utxo(80_000, 2);
let utxo3 = utxo(300_000, 3); let utxo3 = utxo(300_000, 3);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let mut database = MemoryDatabase::default(); let mut database = MemoryDatabase::default();
@ -1070,7 +1078,7 @@ mod test {
fn test_oldest_first_coin_selection_use_all() { fn test_oldest_first_coin_selection_use_all() {
let mut database = MemoryDatabase::default(); let mut database = MemoryDatabase::default();
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database); let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = OldestFirstCoinSelection::default() let result = OldestFirstCoinSelection::default()
@ -1093,7 +1101,7 @@ mod test {
fn test_oldest_first_coin_selection_use_only_necessary() { fn test_oldest_first_coin_selection_use_only_necessary() {
let mut database = MemoryDatabase::default(); let mut database = MemoryDatabase::default();
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database); let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = OldestFirstCoinSelection::default() let result = OldestFirstCoinSelection::default()
@ -1117,7 +1125,7 @@ mod test {
fn test_oldest_first_coin_selection_insufficient_funds() { fn test_oldest_first_coin_selection_insufficient_funds() {
let mut database = MemoryDatabase::default(); let mut database = MemoryDatabase::default();
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database); let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 600_000 + FEE_AMOUNT; let target_amount = 600_000 + FEE_AMOUNT;
OldestFirstCoinSelection::default() OldestFirstCoinSelection::default()
@ -1139,7 +1147,7 @@ mod test {
let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database); let utxos = setup_database_and_get_oldest_first_test_utxos(&mut database);
let target_amount: u64 = utxos.iter().map(|wu| wu.utxo.txout().value).sum::<u64>() - 50; let target_amount: u64 = utxos.iter().map(|wu| wu.utxo.txout().value).sum::<u64>() - 50;
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
OldestFirstCoinSelection::default() OldestFirstCoinSelection::default()
.coin_select( .coin_select(
@ -1160,7 +1168,7 @@ mod test {
let utxos = generate_same_value_utxos(100_000, 20); let utxos = generate_same_value_utxos(100_000, 20);
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 250_000 + FEE_AMOUNT; let target_amount = 250_000 + FEE_AMOUNT;
@ -1184,7 +1192,7 @@ mod test {
fn test_bnb_coin_selection_required_are_enough() { fn test_bnb_coin_selection_required_are_enough() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let result = BranchAndBoundCoinSelection::default() let result = BranchAndBoundCoinSelection::default()
@ -1207,7 +1215,7 @@ mod test {
fn test_bnb_coin_selection_optional_are_enough() { fn test_bnb_coin_selection_optional_are_enough() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 299756 + FEE_AMOUNT; let target_amount = 299756 + FEE_AMOUNT;
let result = BranchAndBoundCoinSelection::default() let result = BranchAndBoundCoinSelection::default()
@ -1241,7 +1249,7 @@ mod test {
assert_eq!(amount, 100_000); assert_eq!(amount, 100_000);
let amount: u64 = optional.iter().map(|u| u.utxo.txout().value).sum(); let amount: u64 = optional.iter().map(|u| u.utxo.txout().value).sum();
assert!(amount > 150_000); assert!(amount > 150_000);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 150_000 + FEE_AMOUNT; let target_amount = 150_000 + FEE_AMOUNT;
@ -1266,7 +1274,7 @@ mod test {
fn test_bnb_coin_selection_insufficient_funds() { fn test_bnb_coin_selection_insufficient_funds() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 500_000 + FEE_AMOUNT; let target_amount = 500_000 + FEE_AMOUNT;
BranchAndBoundCoinSelection::default() BranchAndBoundCoinSelection::default()
@ -1286,7 +1294,7 @@ mod test {
fn test_bnb_coin_selection_insufficient_funds_high_fees() { fn test_bnb_coin_selection_insufficient_funds_high_fees() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 250_000 + FEE_AMOUNT; let target_amount = 250_000 + FEE_AMOUNT;
BranchAndBoundCoinSelection::default() BranchAndBoundCoinSelection::default()
@ -1305,7 +1313,7 @@ mod test {
fn test_bnb_coin_selection_check_fee_rate() { fn test_bnb_coin_selection_check_fee_rate() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 99932; // first utxo's effective value let target_amount = 99932; // first utxo's effective value
let result = BranchAndBoundCoinSelection::new(0) let result = BranchAndBoundCoinSelection::new(0)
@ -1335,7 +1343,7 @@ mod test {
for _i in 0..200 { for _i in 0..200 {
let mut optional_utxos = generate_random_utxos(&mut rng, 16); let mut optional_utxos = generate_random_utxos(&mut rng, 16);
let target_amount = sum_random_utxos(&mut rng, &mut optional_utxos); let target_amount = sum_random_utxos(&mut rng, &mut optional_utxos);
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let result = BranchAndBoundCoinSelection::new(0) let result = BranchAndBoundCoinSelection::new(0)
.coin_select( .coin_select(
&database, &database,
@ -1364,7 +1372,7 @@ mod test {
let size_of_change = 31; let size_of_change = 31;
let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb(); let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
BranchAndBoundCoinSelection::new(size_of_change) BranchAndBoundCoinSelection::new(size_of_change)
.bnb( .bnb(
@ -1395,7 +1403,7 @@ mod test {
let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb(); let cost_of_change = size_of_change as f32 * fee_rate.as_sat_per_vb();
let target_amount = 20_000 + FEE_AMOUNT; let target_amount = 20_000 + FEE_AMOUNT;
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
BranchAndBoundCoinSelection::new(size_of_change) BranchAndBoundCoinSelection::new(size_of_change)
.bnb( .bnb(
@ -1431,7 +1439,7 @@ mod test {
// cost_of_change + 5. // cost_of_change + 5.
let target_amount = 2 * 50_000 - 2 * 67 - cost_of_change.ceil() as i64 + 5; let target_amount = 2 * 50_000 - 2 * 67 - cost_of_change.ceil() as i64 + 5;
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let result = BranchAndBoundCoinSelection::new(size_of_change) let result = BranchAndBoundCoinSelection::new(size_of_change)
.bnb( .bnb(
@ -1471,7 +1479,7 @@ mod test {
let target_amount = let target_amount =
optional_utxos[3].effective_value + optional_utxos[23].effective_value; optional_utxos[3].effective_value + optional_utxos[23].effective_value;
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let result = BranchAndBoundCoinSelection::new(0) let result = BranchAndBoundCoinSelection::new(0)
.bnb( .bnb(
@ -1502,7 +1510,7 @@ mod test {
.map(|u| OutputGroup::new(u, fee_rate)) .map(|u| OutputGroup::new(u, fee_rate))
.collect(); .collect();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let result = BranchAndBoundCoinSelection::default().single_random_draw( let result = BranchAndBoundCoinSelection::default().single_random_draw(
vec![], vec![],
@ -1521,7 +1529,7 @@ mod test {
fn test_bnb_exclude_negative_effective_value() { fn test_bnb_exclude_negative_effective_value() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let selection = BranchAndBoundCoinSelection::default().coin_select( let selection = BranchAndBoundCoinSelection::default().coin_select(
&database, &database,
@ -1545,7 +1553,7 @@ mod test {
fn test_bnb_include_negative_effective_value_when_required() { fn test_bnb_include_negative_effective_value_when_required() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let (required, optional) = utxos let (required, optional) = utxos
.into_iter() .into_iter()
@ -1573,7 +1581,7 @@ mod test {
fn test_bnb_sum_of_effective_value_negative() { fn test_bnb_sum_of_effective_value_negative() {
let utxos = get_test_utxos(); let utxos = get_test_utxos();
let database = MemoryDatabase::default(); let database = MemoryDatabase::default();
let drain_script = Script::default(); let drain_script = ScriptBuf::default();
let selection = BranchAndBoundCoinSelection::default().coin_select( let selection = BranchAndBoundCoinSelection::default().coin_select(
&database, &database,

View File

@ -20,7 +20,7 @@
//! # use bdk::wallet::hardwaresigner::HWISigner; //! # use bdk::wallet::hardwaresigner::HWISigner;
//! # use bdk::wallet::AddressIndex::New; //! # use bdk::wallet::AddressIndex::New;
//! # use bdk::{FeeRate, KeychainKind, SignOptions, SyncOptions, Wallet}; //! # use bdk::{FeeRate, KeychainKind, SignOptions, SyncOptions, Wallet};
//! # use hwi::{types::HWIChain, HWIClient}; //! # use hwi::HWIClient;
//! # use std::sync::Arc; //! # use std::sync::Arc;
//! # //! #
//! # fn main() -> Result<(), Box<dyn std::error::Error>> { //! # fn main() -> Result<(), Box<dyn std::error::Error>> {
@ -29,7 +29,7 @@
//! panic!("No devices found!"); //! panic!("No devices found!");
//! } //! }
//! let first_device = devices.remove(0)?; //! let first_device = devices.remove(0)?;
//! let custom_signer = HWISigner::from_device(&first_device, HWIChain::Test)?; //! let custom_signer = HWISigner::from_device(&first_device, Network::Testnet.into())?;
//! //!
//! # let mut wallet = Wallet::new( //! # let mut wallet = Wallet::new(
//! # "", //! # "",
@ -49,9 +49,9 @@
//! # } //! # }
//! ``` //! ```
use bitcoin::bip32::Fingerprint;
use bitcoin::psbt::PartiallySignedTransaction; use bitcoin::psbt::PartiallySignedTransaction;
use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::util::bip32::Fingerprint;
use hwi::error::Error; use hwi::error::Error;
use hwi::types::{HWIChain, HWIDevice}; use hwi::types::{HWIChain, HWIDevice};

View File

@ -24,10 +24,11 @@ use std::sync::Arc;
use bitcoin::secp256k1::Secp256k1; use bitcoin::secp256k1::Secp256k1;
use bitcoin::consensus::encode::serialize; use bitcoin::consensus::encode::serialize;
use bitcoin::util::psbt; use bitcoin::psbt;
use bitcoin::sighash::{EcdsaSighashType, TapSighashType};
use bitcoin::{ use bitcoin::{
Address, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script, Sequence, absolute, Address, Network, OutPoint, Script, ScriptBuf, Sequence, Transaction, TxOut, Txid,
Transaction, TxOut, Txid, Witness, Weight, Witness,
}; };
use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier}; use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
@ -116,13 +117,15 @@ pub enum AddressIndex {
/// web page. /// web page.
LastUnused, LastUnused,
/// Return the address for a specific descriptor index. Does not change the current descriptor /// Return the address for a specific descriptor index. Does not change the current descriptor
/// index used by `AddressIndex::New` and `AddressIndex::LastUsed`. /// index used by `AddressIndex::New` and `AddressIndex::LastUsed`. The index must be non-hardened,
/// i.e., < 2**31.
/// ///
/// Use with caution, if an index is given that is less than the current descriptor index /// Use with caution, if an index is given that is less than the current descriptor index
/// then the returned address may have already been used. /// then the returned address may have already been used.
Peek(u32), Peek(u32),
/// Return the address for a specific descriptor index and reset the current descriptor index /// Return the address for a specific descriptor index and reset the current descriptor index
/// used by `AddressIndex::New` and `AddressIndex::LastUsed` to this value. /// used by `AddressIndex::New` and `AddressIndex::LastUsed` to this value. The index must be
/// non-hardened, i.e. < 2**31
/// ///
/// Use with caution, if an index is given that is less than the current descriptor index /// Use with caution, if an index is given that is less than the current descriptor index
/// then the returned address and subsequent addresses returned by calls to `AddressIndex::New` /// then the returned address and subsequent addresses returned by calls to `AddressIndex::New`
@ -257,6 +260,7 @@ where
let address_result = self let address_result = self
.get_descriptor_for_keychain(keychain) .get_descriptor_for_keychain(keychain)
.at_derivation_index(incremented_index) .at_derivation_index(incremented_index)
.expect("can't be hardened")
.address(self.network); .address(self.network);
address_result address_result
@ -275,7 +279,8 @@ where
let derived_key = self let derived_key = self
.get_descriptor_for_keychain(keychain) .get_descriptor_for_keychain(keychain)
.at_derivation_index(current_index); .at_derivation_index(current_index)
.expect("can't be hardened");
let script_pubkey = derived_key.script_pubkey(); let script_pubkey = derived_key.script_pubkey();
@ -304,6 +309,7 @@ where
fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> { fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
self.get_descriptor_for_keychain(keychain) self.get_descriptor_for_keychain(keychain)
.at_derivation_index(index) .at_derivation_index(index)
.map_err(|_| Error::HardenedIndex)?
.address(self.network) .address(self.network)
.map(|address| AddressInfo { .map(|address| AddressInfo {
index, index,
@ -320,6 +326,7 @@ where
self.get_descriptor_for_keychain(keychain) self.get_descriptor_for_keychain(keychain)
.at_derivation_index(index) .at_derivation_index(index)
.map_err(|_| Error::HardenedIndex)?
.address(self.network) .address(self.network)
.map(|address| AddressInfo { .map(|address| AddressInfo {
index, index,
@ -567,7 +574,7 @@ where
/// # use bdk::database::*; /// # use bdk::database::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)"; /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let wallet = doctest_wallet!(); /// # let wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// let (psbt, details) = { /// let (psbt, details) = {
/// let mut builder = wallet.build_tx(); /// let mut builder = wallet.build_tx();
/// builder /// builder
@ -669,7 +676,8 @@ where
let current_height = match params.current_height { let current_height = match params.current_height {
// If they didn't tell us the current height, we assume it's the latest sync height. // If they didn't tell us the current height, we assume it's the latest sync height.
None => self.database().get_sync_time()?.map(|sync_time| { None => self.database().get_sync_time()?.map(|sync_time| {
LockTime::from_height(sync_time.block_time.height).expect("Invalid height") absolute::LockTime::from_height(sync_time.block_time.height)
.expect("Invalid height")
}), }),
h => h, h => h,
}; };
@ -680,7 +688,7 @@ where
// Fee sniping can be partially prevented by setting the timelock // Fee sniping can be partially prevented by setting the timelock
// to current_height. If we don't know the current_height, // to current_height. If we don't know the current_height,
// we default to 0. // we default to 0.
let fee_sniping_height = current_height.unwrap_or(LockTime::ZERO); let fee_sniping_height = current_height.unwrap_or(absolute::LockTime::ZERO);
// We choose the biggest between the required nlocktime and the fee sniping // We choose the biggest between the required nlocktime and the fee sniping
// height // height
@ -688,7 +696,7 @@ where
// No requirement, just use the fee_sniping_height // No requirement, just use the fee_sniping_height
None => fee_sniping_height, None => fee_sniping_height,
// There's a block-based requirement, but the value is lower than the fee_sniping_height // There's a block-based requirement, but the value is lower than the fee_sniping_height
Some(value @ LockTime::Blocks(_)) if value < fee_sniping_height => fee_sniping_height, Some(value @ absolute::LockTime::Blocks(_)) if value < fee_sniping_height => fee_sniping_height,
// There's a time-based requirement or a block-based requirement greater // There's a time-based requirement or a block-based requirement greater
// than the fee_sniping_height use that value // than the fee_sniping_height use that value
Some(value) => value, Some(value) => value,
@ -704,7 +712,9 @@ where
let n_sequence = match (params.rbf, requirements.csv) { let n_sequence = match (params.rbf, requirements.csv) {
// No RBF or CSV but there's an nLockTime, so the nSequence cannot be final // No RBF or CSV but there's an nLockTime, so the nSequence cannot be final
(None, None) if lock_time != LockTime::ZERO => Sequence::ENABLE_LOCKTIME_NO_RBF, (None, None) if lock_time != absolute::LockTime::ZERO => {
Sequence::ENABLE_LOCKTIME_NO_RBF
}
// No RBF, CSV or nLockTime, make the transaction final // No RBF, CSV or nLockTime, make the transaction final
(None, None) => Sequence::MAX, (None, None) => Sequence::MAX,
@ -767,7 +777,7 @@ where
let mut tx = Transaction { let mut tx = Transaction {
version, version,
lock_time: lock_time.into(), lock_time,
input: vec![], input: vec![],
output: vec![], output: vec![],
}; };
@ -815,7 +825,7 @@ where
// end up with a transaction with a slightly higher fee rate than the requested one. // end up with a transaction with a slightly higher fee rate than the requested one.
// If, instead, we undershoot, we may end up with a feerate lower than the requested one // If, instead, we undershoot, we may end up with a feerate lower than the requested one
// - we might come up with non broadcastable txs! // - we might come up with non broadcastable txs!
fee_amount += fee_rate.fee_wu(2); fee_amount += fee_rate.fee_wu(Weight::from_wu(2));
if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed if params.change_policy != tx_builder::ChangeSpendPolicy::ChangeAllowed
&& self.change_descriptor.is_none() && self.change_descriptor.is_none()
@ -832,7 +842,7 @@ where
params.drain_wallet, params.drain_wallet,
params.manually_selected_only, params.manually_selected_only,
params.bumping_fee.is_some(), // we mandate confirmed transactions if we're bumping the fee params.bumping_fee.is_some(), // we mandate confirmed transactions if we're bumping the fee
current_height.map(LockTime::to_consensus_u32), current_height.map(absolute::LockTime::to_consensus_u32),
)?; )?;
// get drain script // get drain script
@ -860,7 +870,7 @@ where
.iter() .iter()
.map(|u| bitcoin::TxIn { .map(|u| bitcoin::TxIn {
previous_output: u.outpoint(), previous_output: u.outpoint(),
script_sig: Script::default(), script_sig: ScriptBuf::default(),
sequence: n_sequence, sequence: n_sequence,
witness: Witness::new(), witness: Witness::new(),
}) })
@ -949,7 +959,8 @@ where
/// # use bdk::database::*; /// # use bdk::database::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)"; /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let wallet = doctest_wallet!(); /// # let wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address =
/// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// let (mut psbt, _) = { /// let (mut psbt, _) = {
/// let mut builder = wallet.build_tx(); /// let mut builder = wallet.build_tx();
/// builder /// builder
@ -963,7 +974,7 @@ where
/// let (mut psbt, _) = { /// let (mut psbt, _) = {
/// let mut builder = wallet.build_fee_bump(tx.txid())?; /// let mut builder = wallet.build_fee_bump(tx.txid())?;
/// builder /// builder
/// .fee_rate(FeeRate::from_sat_per_vb(5.0)); /// .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0));
/// builder.finish()? /// builder.finish()?
/// }; /// };
/// ///
@ -1010,6 +1021,7 @@ where
.borrow() .borrow()
.get_path_from_script_pubkey(&txout.script_pubkey)? .get_path_from_script_pubkey(&txout.script_pubkey)?
{ {
#[allow(deprecated)]
Some((keychain, _)) => ( Some((keychain, _)) => (
self._get_descriptor_for_keychain(keychain) self._get_descriptor_for_keychain(keychain)
.0 .0
@ -1100,7 +1112,7 @@ where
/// # use bdk::database::*; /// # use bdk::database::*;
/// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)"; /// # let descriptor = "wpkh(tpubD6NzVbkrYhZ4Xferm7Pz4VnjdcDPFyjVu5K4iZXQ4pVN8Cks4pHVowTBXBKRhX64pkRyJZJN5xAKj4UDNnLPb5p2sSKXhewoYx5GbTdUFWq/*)";
/// # let wallet = doctest_wallet!(); /// # let wallet = doctest_wallet!();
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// let (mut psbt, _) = { /// let (mut psbt, _) = {
/// let mut builder = wallet.build_tx(); /// let mut builder = wallet.build_tx();
/// builder.add_recipient(to_address.script_pubkey(), 50_000); /// builder.add_recipient(to_address.script_pubkey(), 50_000);
@ -1137,8 +1149,8 @@ where
&& !psbt.inputs.iter().all(|i| { && !psbt.inputs.iter().all(|i| {
i.sighash_type.is_none() i.sighash_type.is_none()
|| i.sighash_type == Some(EcdsaSighashType::All.into()) || i.sighash_type == Some(EcdsaSighashType::All.into())
|| i.sighash_type == Some(SchnorrSighashType::All.into()) || i.sighash_type == Some(TapSighashType::All.into())
|| i.sighash_type == Some(SchnorrSighashType::Default.into()) || i.sighash_type == Some(TapSighashType::Default.into())
}) })
{ {
return Err(Error::Signer(signer::SignerError::NonStandardSighash)); return Err(Error::Signer(signer::SignerError::NonStandardSighash));
@ -1323,7 +1335,10 @@ where
.borrow() .borrow()
.get_path_from_script_pubkey(&txout.script_pubkey)? .get_path_from_script_pubkey(&txout.script_pubkey)?
.map(|(keychain, child)| (self.get_descriptor_for_keychain(keychain), child)) .map(|(keychain, child)| (self.get_descriptor_for_keychain(keychain), child))
.map(|(desc, child)| desc.at_derivation_index(child))) .map(|(desc, child)| {
desc.at_derivation_index(child)
.expect("child is not hardened")
}))
} }
fn fetch_and_increment_index(&self, keychain: KeychainKind) -> Result<u32, Error> { fn fetch_and_increment_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
@ -1384,7 +1399,10 @@ where
let start_time = time::Instant::new(); let start_time = time::Instant::new();
for i in from..(from + count) { for i in from..(from + count) {
address_batch.set_script_pubkey( address_batch.set_script_pubkey(
&descriptor.at_derivation_index(i).script_pubkey(), &descriptor
.at_derivation_index(i)
.expect("i is not hardened")
.script_pubkey(),
keychain, keychain,
i, i,
)?; )?;
@ -1410,6 +1428,7 @@ where
let keychain = utxo.keychain; let keychain = utxo.keychain;
( (
utxo, utxo,
#[allow(deprecated)]
self.get_descriptor_for_keychain(keychain) self.get_descriptor_for_keychain(keychain)
.max_satisfaction_weight() .max_satisfaction_weight()
.unwrap(), .unwrap(),
@ -1614,7 +1633,9 @@ where
}; };
let desc = self.get_descriptor_for_keychain(keychain); let desc = self.get_descriptor_for_keychain(keychain);
let derived_descriptor = desc.at_derivation_index(child); let derived_descriptor = desc
.at_derivation_index(child)
.expect("child can't be hardened");
psbt_input psbt_input
.update_with_descriptor_unchecked(&derived_descriptor) .update_with_descriptor_unchecked(&derived_descriptor)
@ -1665,7 +1686,9 @@ where
); );
let desc = self.get_descriptor_for_keychain(keychain); let desc = self.get_descriptor_for_keychain(keychain);
let desc = desc.at_derivation_index(child); let desc = desc
.at_derivation_index(child)
.expect("child can't be hardened");
if is_input { if is_input {
psbt.update_input_with_descriptor(index, &desc) psbt.update_input_with_descriptor(index, &desc)
@ -1830,6 +1853,7 @@ pub fn get_funded_wallet(
.set_script_pubkey( .set_script_pubkey(
&bitcoin::Address::from_str(&tx_meta.output.get(0).unwrap().to_address) &bitcoin::Address::from_str(&tx_meta.output.get(0).unwrap().to_address)
.unwrap() .unwrap()
.assume_checked()
.script_pubkey(), .script_pubkey(),
KeychainKind::External, KeychainKind::External,
funding_address_kix, funding_address_kix,
@ -1849,7 +1873,7 @@ pub fn get_funded_wallet(
#[cfg(test)] #[cfg(test)]
pub(crate) mod test { pub(crate) mod test {
use assert_matches::assert_matches; use assert_matches::assert_matches;
use bitcoin::{util::psbt, Network, PackedLockTime, Sequence}; use bitcoin::{absolute, blockdata::script::PushBytes, psbt, Network, Sequence};
use crate::database::Database; use crate::database::Database;
use crate::types::KeychainKind; use crate::types::KeychainKind;
@ -2185,7 +2209,7 @@ pub(crate) mod test {
// Since we never synced the wallet we don't have a last_sync_height // Since we never synced the wallet we don't have a last_sync_height
// we could use to try to prevent fee sniping. We default to 0. // we could use to try to prevent fee sniping. We default to 0.
assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(0)); assert_eq!(psbt.unsigned_tx.lock_time, absolute::LockTime::ZERO);
} }
#[test] #[test]
@ -2210,7 +2234,10 @@ pub(crate) mod test {
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
// current_height will override the last sync height // current_height will override the last sync height
assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(current_height)); assert_eq!(
psbt.unsigned_tx.lock_time,
absolute::LockTime::from_height(current_height).unwrap()
);
} }
#[test] #[test]
@ -2235,7 +2262,7 @@ pub(crate) mod test {
// If there's no current_height we're left with using the last sync height // If there's no current_height we're left with using the last sync height
assert_eq!( assert_eq!(
psbt.unsigned_tx.lock_time, psbt.unsigned_tx.lock_time,
PackedLockTime(sync_time.block_time.height) absolute::LockTime::from_height(sync_time.block_time.height).unwrap()
); );
} }
@ -2247,7 +2274,10 @@ pub(crate) mod test {
builder.add_recipient(addr.script_pubkey(), 25_000); builder.add_recipient(addr.script_pubkey(), 25_000);
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(100_000)); assert_eq!(
psbt.unsigned_tx.lock_time,
absolute::LockTime::from_height(100_000).unwrap()
);
} }
#[test] #[test]
@ -2258,13 +2288,16 @@ pub(crate) mod test {
builder builder
.add_recipient(addr.script_pubkey(), 25_000) .add_recipient(addr.script_pubkey(), 25_000)
.current_height(630_001) .current_height(630_001)
.nlocktime(LockTime::from_height(630_000).unwrap()); .nlocktime(absolute::LockTime::from_height(630_000).unwrap());
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
// When we explicitly specify a nlocktime // When we explicitly specify a nlocktime
// we don't try any fee sniping prevention trick // we don't try any fee sniping prevention trick
// (we ignore the current_height) // (we ignore the current_height)
assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(630_000)); assert_eq!(
psbt.unsigned_tx.lock_time,
absolute::LockTime::from_height(630_000).unwrap()
);
} }
#[test] #[test]
@ -2274,10 +2307,13 @@ pub(crate) mod test {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 25_000) .add_recipient(addr.script_pubkey(), 25_000)
.nlocktime(LockTime::from_height(630_000).unwrap()); .nlocktime(absolute::LockTime::from_height(630_000).unwrap());
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(630_000)); assert_eq!(
psbt.unsigned_tx.lock_time,
absolute::LockTime::from_height(630_000).unwrap()
);
} }
#[test] #[test]
@ -2290,7 +2326,7 @@ pub(crate) mod test {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 25_000) .add_recipient(addr.script_pubkey(), 25_000)
.nlocktime(LockTime::from_height(50000).unwrap()); .nlocktime(absolute::LockTime::from_height(50000).unwrap());
builder.finish().unwrap(); builder.finish().unwrap();
} }
@ -2428,7 +2464,9 @@ pub(crate) mod test {
#[test] #[test]
fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() { fn test_create_tx_drain_wallet_and_drain_to_and_with_recipient() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); let addr = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
.unwrap()
.assume_checked();
let drain_addr = wallet.get_address(New).unwrap(); let drain_addr = wallet.get_address(New).unwrap();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
@ -2645,19 +2683,18 @@ pub(crate) mod test {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
.sighash(bitcoin::EcdsaSighashType::Single.into()); .sighash(bitcoin::sighash::EcdsaSighashType::Single.into());
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
assert_eq!( assert_eq!(
psbt.inputs[0].sighash_type, psbt.inputs[0].sighash_type,
Some(bitcoin::EcdsaSighashType::Single.into()) Some(bitcoin::sighash::EcdsaSighashType::Single.into())
); );
} }
#[test] #[test]
fn test_create_tx_input_hd_keypaths() { fn test_create_tx_input_hd_keypaths() {
use bitcoin::util::bip32::{DerivationPath, Fingerprint}; use bitcoin::bip32::{DerivationPath, Fingerprint};
use std::str::FromStr;
let (wallet, _, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)"); let (wallet, _, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
let addr = wallet.get_address(New).unwrap(); let addr = wallet.get_address(New).unwrap();
@ -2677,8 +2714,7 @@ pub(crate) mod test {
#[test] #[test]
fn test_create_tx_output_hd_keypaths() { fn test_create_tx_output_hd_keypaths() {
use bitcoin::util::bip32::{DerivationPath, Fingerprint}; use bitcoin::bip32::{DerivationPath, Fingerprint};
use std::str::FromStr;
let (wallet, descriptors, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)"); let (wallet, descriptors, _) = get_funded_wallet("wpkh([d34db33f/44'/0'/0']tpubDEnoLuPdBep9bzw5LoGYpsxUQYheRQ9gcgrJhJEcdKFB9cWQRyYmkCyRoTqeD4tJYiVVgt6A3rN6rWn9RYhR9sBsGxji29LYWHuKKbdb1ev/0/*)");
// cache some addresses // cache some addresses
@ -2712,7 +2748,7 @@ pub(crate) mod test {
assert_eq!( assert_eq!(
psbt.inputs[0].redeem_script, psbt.inputs[0].redeem_script,
Some(Script::from( Some(ScriptBuf::from(
Vec::<u8>::from_hex( Vec::<u8>::from_hex(
"21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac" "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
) )
@ -2736,7 +2772,7 @@ pub(crate) mod test {
assert_eq!(psbt.inputs[0].redeem_script, None); assert_eq!(psbt.inputs[0].redeem_script, None);
assert_eq!( assert_eq!(
psbt.inputs[0].witness_script, psbt.inputs[0].witness_script,
Some(Script::from( Some(ScriptBuf::from(
Vec::<u8>::from_hex( Vec::<u8>::from_hex(
"21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac" "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac"
) )
@ -2756,7 +2792,7 @@ pub(crate) mod test {
builder.drain_to(addr.script_pubkey()).drain_wallet(); builder.drain_to(addr.script_pubkey()).drain_wallet();
let (psbt, _) = builder.finish().unwrap(); let (psbt, _) = builder.finish().unwrap();
let script = Script::from( let script = ScriptBuf::from(
Vec::<u8>::from_hex( Vec::<u8>::from_hex(
"21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac", "21032b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3ac",
) )
@ -2830,7 +2866,9 @@ pub(crate) mod test {
Some(100), Some(100),
); );
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -2859,7 +2897,9 @@ pub(crate) mod test {
Some(100), Some(100),
); );
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -2877,7 +2917,9 @@ pub(crate) mod test {
fn test_create_tx_policy_path_required() { fn test_create_tx_policy_path_required() {
let (wallet, _, _) = get_funded_wallet(get_test_a_or_b_plus_csv()); let (wallet, _, _) = get_funded_wallet(get_test_a_or_b_plus_csv());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.add_recipient(addr.script_pubkey(), 30_000); builder.add_recipient(addr.script_pubkey(), 30_000);
builder.finish().unwrap(); builder.finish().unwrap();
@ -2907,7 +2949,9 @@ pub(crate) mod test {
// child #0 is just the key "A" // child #0 is just the key "A"
let path = vec![(root_id, vec![0])].into_iter().collect(); let path = vec![(root_id, vec![0])].into_iter().collect();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -2926,7 +2970,9 @@ pub(crate) mod test {
// child #1 is or(pk(B),older(144)) // child #1 is or(pk(B),older(144))
let path = vec![(root_id, vec![1])].into_iter().collect(); let path = vec![(root_id, vec![1])].into_iter().collect();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -2945,7 +2991,9 @@ pub(crate) mod test {
// child #0 is pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu) // child #0 is pk(cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)
let path = vec![(root_id, vec![0])].into_iter().collect(); let path = vec![(root_id, vec![0])].into_iter().collect();
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 30_000) .add_recipient(addr.script_pubkey(), 30_000)
@ -2957,8 +3005,8 @@ pub(crate) mod test {
#[test] #[test]
fn test_create_tx_global_xpubs_with_origin() { fn test_create_tx_global_xpubs_with_origin() {
use bitcoin::bip32;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::util::bip32;
let (wallet, _, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)"); let (wallet, _, _) = get_funded_wallet("wpkh([73756c7f/48'/0'/0'/2']tpubDCKxNyM3bLgbEX13Mcd8mYxbVg9ajDkWXMh29hMWBurKfVmBfWAM96QVP3zaUcN51HvkZ3ar4VwP82kC8JZhhux8vFQoJintSpVBwpFvyU3/0/*)");
let addr = wallet.get_address(New).unwrap(); let addr = wallet.get_address(New).unwrap();
@ -2982,8 +3030,11 @@ pub(crate) mod test {
let (wallet2, _, _) = let (wallet2, _, _) =
get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)"); get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let utxo = wallet2.list_unspent().unwrap().remove(0); let utxo = wallet2.list_unspent().unwrap().remove(0);
#[allow(deprecated)]
let foreign_utxo_satisfaction = wallet2 let foreign_utxo_satisfaction = wallet2
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -3049,6 +3100,7 @@ pub(crate) mod test {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
let outpoint = wallet.list_unspent().unwrap()[0].outpoint; let outpoint = wallet.list_unspent().unwrap()[0].outpoint;
#[allow(deprecated)]
let foreign_utxo_satisfaction = wallet let foreign_utxo_satisfaction = wallet
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -3082,6 +3134,7 @@ pub(crate) mod test {
.transaction .transaction
.unwrap(); .unwrap();
#[allow(deprecated)]
let satisfaction_weight = wallet2 let satisfaction_weight = wallet2
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -3121,9 +3174,12 @@ pub(crate) mod test {
let (wallet1, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet1, _, _) = get_funded_wallet(get_test_wpkh());
let (wallet2, _, txid2) = let (wallet2, _, txid2) =
get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)"); get_funded_wallet("wpkh(cVbZ8ovhye9AoAHFsqobCf7LxbXDAECy9Kb8TZdfsDYMZGBUyCnm)");
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let utxo2 = wallet2.list_unspent().unwrap().remove(0); let utxo2 = wallet2.list_unspent().unwrap().remove(0);
#[allow(deprecated)]
let satisfaction_weight = wallet2 let satisfaction_weight = wallet2
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -3213,8 +3269,8 @@ pub(crate) mod test {
#[test] #[test]
fn test_create_tx_global_xpubs_master_without_origin() { fn test_create_tx_global_xpubs_master_without_origin() {
use bitcoin::bip32;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::util::bip32;
let (wallet, _, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)"); let (wallet, _, _) = get_funded_wallet("wpkh(tpubD6NzVbkrYhZ4Y55A58Gv9RSNF5hy84b5AJqYy7sCcjFrkcLpPre8kmgfit6kY1Zs3BLgeypTDBZJM222guPpdz7Cup5yzaMu62u7mYGbwFL/0/*)");
let addr = wallet.get_address(New).unwrap(); let addr = wallet.get_address(New).unwrap();
@ -3343,7 +3399,9 @@ pub(crate) mod test {
#[test] #[test]
fn test_bump_fee_reduce_change() { fn test_bump_fee_reduce_change() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 25_000) .add_recipient(addr.script_pubkey(), 25_000)
@ -3403,7 +3461,9 @@ pub(crate) mod test {
#[test] #[test]
fn test_bump_fee_absolute_reduce_change() { fn test_bump_fee_absolute_reduce_change() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 25_000) .add_recipient(addr.script_pubkey(), 25_000)
@ -3469,7 +3529,9 @@ pub(crate) mod test {
#[test] #[test]
fn test_bump_fee_reduce_single_recipient() { fn test_bump_fee_reduce_single_recipient() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
@ -3513,7 +3575,9 @@ pub(crate) mod test {
#[test] #[test]
fn test_bump_fee_absolute_reduce_single_recipient() { fn test_bump_fee_absolute_reduce_single_recipient() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
@ -3567,7 +3631,9 @@ pub(crate) mod test {
txid: incoming_txid, txid: incoming_txid,
vout: 0, vout: 0,
}; };
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
@ -3624,7 +3690,9 @@ pub(crate) mod test {
txid: incoming_txid, txid: incoming_txid,
vout: 0, vout: 0,
}; };
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
@ -3667,7 +3735,9 @@ pub(crate) mod test {
Some(100), Some(100),
); );
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -3730,7 +3800,9 @@ pub(crate) mod test {
Some(100), Some(100),
); );
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -3794,7 +3866,9 @@ pub(crate) mod test {
); );
// initially make a tx without change by using `drain_to` // initially make a tx without change by using `drain_to`
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
@ -3870,7 +3944,9 @@ pub(crate) mod test {
Some(100), Some(100),
); );
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -3907,7 +3983,7 @@ pub(crate) mod test {
// + extra input weight: 160 WU = (32 (prevout) + 4 (vout) + 4 (nsequence)) * 4 // + extra input weight: 160 WU = (32 (prevout) + 4 (vout) + 4 (nsequence)) * 4
// + input satisfaction weight: 112 WU = 106 (witness) + 2 (witness len) + (1 (script len)) * 4 // + input satisfaction weight: 112 WU = 106 (witness) + 2 (witness len) + (1 (script len)) * 4
// - change output weight: 124 WU = (8 (value) + 1 (script len) + 22 (script)) * 4 // - change output weight: 124 WU = (8 (value) + 1 (script len) + 22 (script)) * 4
let new_tx_weight = original_tx_weight + 160 + 112 - 124; let new_tx_weight = original_tx_weight + Weight::from_wu(160 + 112 - 124);
// two inputs (50k, 25k) and one output (45k) - epsilon // two inputs (50k, 25k) and one output (45k) - epsilon
// We use epsilon here to avoid asking for a slightly too high feerate // We use epsilon here to avoid asking for a slightly too high feerate
let fee_abs = 50_000 + 25_000 - 45_000 - 10; let fee_abs = 50_000 + 25_000 - 45_000 - 10;
@ -3947,7 +4023,9 @@ pub(crate) mod test {
Some(100), Some(100),
); );
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -4018,7 +4096,9 @@ pub(crate) mod test {
Some(100), Some(100),
); );
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -4090,7 +4170,9 @@ pub(crate) mod test {
// The replacement transaction may only include an unconfirmed input // The replacement transaction may only include an unconfirmed input
// if that input was included in one of the original transactions. // if that input was included in one of the original transactions.
let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh()); let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_wallet() .drain_wallet()
@ -4134,7 +4216,9 @@ pub(crate) mod test {
// always fee bump with an unconfirmed input if it was included in the // always fee bump with an unconfirmed input if it was included in the
// original transaction) // original transaction)
let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh()); let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
// We receive a tx with 0 confirmations, which will be used as an input // We receive a tx with 0 confirmations, which will be used as an input
// in the drain tx. // in the drain tx.
crate::populate_test_db!( crate::populate_test_db!(
@ -4184,7 +4268,9 @@ pub(crate) mod test {
// for a transaction. // for a transaction.
// See https://github.com/bitcoindevkit/bdk/issues/660 // See https://github.com/bitcoindevkit/bdk/issues/660
let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh()); let (wallet, descriptors, _) = get_funded_wallet(get_test_wpkh());
let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt").unwrap(); let send_to = Address::from_str("tb1ql7w62elx9ucw4pj5lgw4l028hmuw80sndtntxt")
.unwrap()
.assume_checked();
let fee_rate = FeeRate::from_sat_per_vb(2.01); let fee_rate = FeeRate::from_sat_per_vb(2.01);
let incoming_txid = crate::populate_test_db!( let incoming_txid = crate::populate_test_db!(
wallet.database.borrow_mut(), wallet.database.borrow_mut(),
@ -4302,7 +4388,9 @@ pub(crate) mod test {
#[test] #[test]
fn test_include_output_redeem_witness_script() { fn test_include_output_redeem_witness_script() {
let (wallet, _, _) = get_funded_wallet("sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))"); let (wallet, _, _) = get_funded_wallet("sh(wsh(multi(1,cVpPVruEDdmutPzisEsYvtST1usBR3ntr8pXSyt6D2YYqXRyPcFW,cRjo6jqfVNP33HhSS76UhXETZsGTZYx8FMFvR9kpbtCSV1PmdZdu)))");
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -4319,7 +4407,9 @@ pub(crate) mod test {
#[test] #[test]
fn test_signing_only_one_of_multiple_inputs() { fn test_signing_only_one_of_multiple_inputs() {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), 45_000) .add_recipient(addr.script_pubkey(), 45_000)
@ -4327,7 +4417,7 @@ pub(crate) mod test {
let (mut psbt, _) = builder.finish().unwrap(); let (mut psbt, _) = builder.finish().unwrap();
// add another input to the psbt that is at least passable. // add another input to the psbt that is at least passable.
let dud_input = bitcoin::util::psbt::Input { let dud_input = bitcoin::psbt::Input {
witness_utxo: Some(TxOut { witness_utxo: Some(TxOut {
value: 100_000, value: 100_000,
script_pubkey: miniscript::Descriptor::<bitcoin::PublicKey>::from_str( script_pubkey: miniscript::Descriptor::<bitcoin::PublicKey>::from_str(
@ -4619,7 +4709,9 @@ pub(crate) mod test {
wallet.get_address(New).unwrap(), wallet.get_address(New).unwrap(),
AddressInfo { AddressInfo {
index: 0, index: 0,
address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a").unwrap(), address: Address::from_str("tb1q6yn66vajcctph75pvylgkksgpp6nq04ppwct9a")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -4629,7 +4721,9 @@ pub(crate) mod test {
wallet.get_address(New).unwrap(), wallet.get_address(New).unwrap(),
AddressInfo { AddressInfo {
index: 1, index: 1,
address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7").unwrap(), address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -4639,7 +4733,9 @@ pub(crate) mod test {
wallet.get_address(Peek(25)).unwrap(), wallet.get_address(Peek(25)).unwrap(),
AddressInfo { AddressInfo {
index: 25, index: 25,
address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2").unwrap(), address: Address::from_str("tb1qsp7qu0knx3sl6536dzs0703u2w2ag6ppl9d0c2")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -4649,7 +4745,9 @@ pub(crate) mod test {
wallet.get_address(New).unwrap(), wallet.get_address(New).unwrap(),
AddressInfo { AddressInfo {
index: 2, index: 2,
address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2").unwrap(), address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -4659,7 +4757,9 @@ pub(crate) mod test {
wallet.get_address(Reset(1)).unwrap(), wallet.get_address(Reset(1)).unwrap(),
AddressInfo { AddressInfo {
index: 1, index: 1,
address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7").unwrap(), address: Address::from_str("tb1q4er7kxx6sssz3q7qp7zsqsdx4erceahhax77d7")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -4669,7 +4769,9 @@ pub(crate) mod test {
wallet.get_address(New).unwrap(), wallet.get_address(New).unwrap(),
AddressInfo { AddressInfo {
index: 2, index: 2,
address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2").unwrap(), address: Address::from_str("tb1qzntf2mqex4ehwkjlfdyy3ewdlk08qkvkvrz7x2")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -4680,7 +4782,8 @@ pub(crate) mod test {
let (wallet, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet, _, _) = get_funded_wallet(get_test_wpkh());
let addr = let addr =
Address::from_str("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c") Address::from_str("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c")
.unwrap(); .unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder.add_recipient(addr.script_pubkey(), 45_000); builder.add_recipient(addr.script_pubkey(), 45_000);
builder.finish().unwrap(); builder.finish().unwrap();
@ -4689,7 +4792,7 @@ pub(crate) mod test {
#[test] #[test]
fn test_get_address() { fn test_get_address() {
use crate::descriptor::template::Bip84; use crate::descriptor::template::Bip84;
let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
let wallet = Wallet::new( let wallet = Wallet::new(
Bip84(key, KeychainKind::External), Bip84(key, KeychainKind::External),
Some(Bip84(key, KeychainKind::Internal)), Some(Bip84(key, KeychainKind::Internal)),
@ -4702,7 +4805,9 @@ pub(crate) mod test {
wallet.get_address(AddressIndex::New).unwrap(), wallet.get_address(AddressIndex::New).unwrap(),
AddressInfo { AddressInfo {
index: 0, index: 0,
address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w").unwrap(), address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w")
.unwrap()
.assume_checked(),
keychain: KeychainKind::External, keychain: KeychainKind::External,
} }
); );
@ -4711,7 +4816,9 @@ pub(crate) mod test {
wallet.get_internal_address(AddressIndex::New).unwrap(), wallet.get_internal_address(AddressIndex::New).unwrap(),
AddressInfo { AddressInfo {
index: 0, index: 0,
address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79").unwrap(), address: Address::from_str("bcrt1q0ue3s5y935tw7v3gmnh36c5zzsaw4n9c9smq79")
.unwrap()
.assume_checked(),
keychain: KeychainKind::Internal, keychain: KeychainKind::Internal,
} }
); );
@ -4728,7 +4835,9 @@ pub(crate) mod test {
wallet.get_internal_address(AddressIndex::New).unwrap(), wallet.get_internal_address(AddressIndex::New).unwrap(),
AddressInfo { AddressInfo {
index: 0, index: 0,
address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w").unwrap(), address: Address::from_str("bcrt1qrhgaqu0zvf5q2d0gwwz04w0dh0cuehhqvzpp4w")
.unwrap()
.assume_checked(),
keychain: KeychainKind::Internal, keychain: KeychainKind::Internal,
}, },
"when there's no internal descriptor it should just use external" "when there's no internal descriptor it should just use external"
@ -4740,7 +4849,7 @@ pub(crate) mod test {
use crate::descriptor::template::Bip84; use crate::descriptor::template::Bip84;
use std::collections::HashSet; use std::collections::HashSet;
let key = bitcoin::util::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap(); let key = bitcoin::bip32::ExtendedPrivKey::from_str("tprv8ZgxMBicQKsPcx5nBGsR63Pe8KnRUqmbJNENAfGftF3yuXoMMoVJJcYeUw5eVkm9WBPjWYt6HMWYJNesB5HaNVBaFc1M6dRjWSYnmewUMYy").unwrap();
let wallet = Wallet::new( let wallet = Wallet::new(
Bip84(key, KeychainKind::External), Bip84(key, KeychainKind::External),
None, None,
@ -4803,7 +4912,7 @@ pub(crate) mod test {
let (wallet, _, _) = get_funded_wallet(get_test_tr_repeated_key()); let (wallet, _, _) = get_funded_wallet(get_test_tr_repeated_key());
let addr = wallet.get_address(AddressIndex::New).unwrap(); let addr = wallet.get_address(AddressIndex::New).unwrap();
let path = vec![("e5mmg3xh".to_string(), vec![0])] let path = vec![("rn4nre9c".to_string(), vec![0])]
.into_iter() .into_iter()
.collect(); .collect();
@ -4823,13 +4932,6 @@ pub(crate) mod test {
assert_eq!( assert_eq!(
input_key_origins, input_key_origins,
vec![ vec![
(
from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
(
vec![],
(FromStr::from_str("871fd295").unwrap(), vec![].into())
)
),
( (
from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"), from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"),
( (
@ -4843,7 +4945,14 @@ pub(crate) mod test {
], ],
(FromStr::from_str("ece52657").unwrap(), vec![].into()) (FromStr::from_str("ece52657").unwrap(), vec![].into())
) )
) ),
(
from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
(
vec![],
(FromStr::from_str("871fd295").unwrap(), vec![].into())
)
),
], ],
"Wrong input tap_key_origins" "Wrong input tap_key_origins"
); );
@ -4863,10 +4972,8 @@ pub(crate) mod test {
#[test] #[test]
fn test_taproot_psbt_input_tap_tree() { fn test_taproot_psbt_input_tap_tree() {
use crate::bitcoin::psbt::serialize::Deserialize;
use crate::bitcoin::psbt::TapTree;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use bitcoin::util::taproot; use bitcoin::taproot;
let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree()); let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree());
let addr = wallet.get_address(AddressIndex::Peek(0)).unwrap(); let addr = wallet.get_address(AddressIndex::Peek(0)).unwrap();
@ -4878,7 +4985,7 @@ pub(crate) mod test {
assert_eq!( assert_eq!(
psbt.inputs[0].tap_merkle_root, psbt.inputs[0].tap_merkle_root,
Some( Some(
FromHex::from_hex( taproot::TapNodeHash::from_str(
"61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986" "61f81509635053e52d9d1217545916167394490da2287aca4693606e43851986"
) )
.unwrap() .unwrap()
@ -4887,9 +4994,9 @@ pub(crate) mod test {
assert_eq!( assert_eq!(
psbt.inputs[0].tap_scripts.clone().into_iter().collect::<Vec<_>>(), psbt.inputs[0].tap_scripts.clone().into_iter().collect::<Vec<_>>(),
vec![ vec![
(taproot::ControlBlock::from_slice(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2").unwrap()).unwrap(), (from_str!("208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac"), taproot::LeafVersion::TapScript)), (taproot::ControlBlock::decode(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b7ef769a745e625ed4b9a4982a4dc08274c59187e73e6f07171108f455081cb2").unwrap()).unwrap(), (ScriptBuf::from_hex("208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac").unwrap(), taproot::LeafVersion::TapScript)),
(taproot::ControlBlock::from_slice(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b9a515f7be31a70186e3c5937ee4a70cc4b4e1efe876c1d38e408222ffc64834").unwrap()).unwrap(), (from_str!("2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac"), taproot::LeafVersion::TapScript)), (taproot::ControlBlock::decode(&Vec::<u8>::from_hex("c0b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55b9a515f7be31a70186e3c5937ee4a70cc4b4e1efe876c1d38e408222ffc64834").unwrap()).unwrap(), (ScriptBuf::from_hex("2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac").unwrap(), taproot::LeafVersion::TapScript)),
], ],
); );
assert_eq!( assert_eq!(
psbt.inputs[0].tap_internal_key, psbt.inputs[0].tap_internal_key,
@ -4905,10 +5012,8 @@ pub(crate) mod test {
psbt.outputs[0].tap_internal_key psbt.outputs[0].tap_internal_key
); );
assert_eq!( let tap_tree: bitcoin::taproot::TapTree = serde_json::from_str(r#"[1,{"Script":["2051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac",192]},1,{"Script":["208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac",192]}]"#).unwrap();
psbt.outputs[0].tap_tree, assert_eq!(psbt.outputs[0].tap_tree, Some(tap_tree));
Some(TapTree::deserialize(&Vec::<u8>::from_hex("01c022208aee2b8120a5f157f1223f72b5e62b825831a27a9fdf427db7cc697494d4a642ac01c0222051494dc22e24a32fe9dcfbd7e85faf345fa1df296fb49d156e859ef345201295ac",).unwrap()).unwrap())
);
} }
#[test] #[test]
@ -4979,9 +5084,12 @@ pub(crate) mod test {
let (wallet1, _, _) = get_funded_wallet(get_test_wpkh()); let (wallet1, _, _) = get_funded_wallet(get_test_wpkh());
let (wallet2, _, _) = get_funded_wallet(get_test_tr_single_sig()); let (wallet2, _, _) = get_funded_wallet(get_test_tr_single_sig());
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let utxo = wallet2.list_unspent().unwrap().remove(0); let utxo = wallet2.list_unspent().unwrap().remove(0);
let psbt_input = wallet2.get_psbt_input(utxo.clone(), None, false).unwrap(); let psbt_input = wallet2.get_psbt_input(utxo.clone(), None, false).unwrap();
#[allow(deprecated)]
let foreign_utxo_satisfaction = wallet2 let foreign_utxo_satisfaction = wallet2
.get_descriptor_for_keychain(KeychainKind::External) .get_descriptor_for_keychain(KeychainKind::External)
.max_satisfaction_weight() .max_satisfaction_weight()
@ -5102,7 +5210,7 @@ pub(crate) mod test {
#[test] #[test]
fn test_taproot_script_spend_sign_include_some_leaves() { fn test_taproot_script_spend_sign_include_some_leaves() {
use crate::signer::TapLeavesOptions; use crate::signer::TapLeavesOptions;
use bitcoin::util::taproot::TapLeafHash; use bitcoin::taproot::TapLeafHash;
let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv()); let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
let addr = wallet.get_address(AddressIndex::New).unwrap(); let addr = wallet.get_address(AddressIndex::New).unwrap();
@ -5144,7 +5252,7 @@ pub(crate) mod test {
#[test] #[test]
fn test_taproot_script_spend_sign_exclude_some_leaves() { fn test_taproot_script_spend_sign_exclude_some_leaves() {
use crate::signer::TapLeavesOptions; use crate::signer::TapLeavesOptions;
use bitcoin::util::taproot::TapLeafHash; use bitcoin::taproot::TapLeafHash;
let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv()); let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
let addr = wallet.get_address(AddressIndex::New).unwrap(); let addr = wallet.get_address(AddressIndex::New).unwrap();
@ -5240,7 +5348,7 @@ pub(crate) mod test {
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
.sighash(SchnorrSighashType::All.into()) .sighash(TapSighashType::All.into())
.drain_wallet(); .drain_wallet();
let (mut psbt, _) = builder.finish().unwrap(); let (mut psbt, _) = builder.finish().unwrap();
@ -5253,7 +5361,7 @@ pub(crate) mod test {
#[test] #[test]
fn test_taproot_sign_non_default_sighash() { fn test_taproot_sign_non_default_sighash() {
let sighash = SchnorrSighashType::NonePlusAnyoneCanPay; let sighash = TapSighashType::NonePlusAnyoneCanPay;
let (wallet, _, _) = get_funded_wallet(get_test_tr_single_sig()); let (wallet, _, _) = get_funded_wallet(get_test_tr_single_sig());
let addr = wallet.get_address(New).unwrap(); let addr = wallet.get_address(New).unwrap();
@ -5367,7 +5475,9 @@ pub(crate) mod test {
// We try to create a transaction, only to notice that all // We try to create a transaction, only to notice that all
// our funds are unspendable // our funds are unspendable
let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX").unwrap(); let addr = Address::from_str("2N1Ffz3WaNzbeLFBb51xyFMHYSEUXcbiSoX")
.unwrap()
.assume_checked();
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
builder builder
.add_recipient(addr.script_pubkey(), balance.immature / 2) .add_recipient(addr.script_pubkey(), balance.immature / 2)
@ -5453,7 +5563,7 @@ pub(crate) mod test {
let addr = wallet.get_address(New).unwrap(); let addr = wallet.get_address(New).unwrap();
let fee_rate = FeeRate::from_sat_per_vb(1.0); let fee_rate = FeeRate::from_sat_per_vb(1.0);
let mut builder = wallet.build_tx(); let mut builder = wallet.build_tx();
let mut data = vec![0]; let data: &PushBytes = From::<&[u8; 1]>::from(&[0; 1]);
builder builder
.drain_to(addr.script_pubkey()) .drain_to(addr.script_pubkey())
.drain_wallet() .drain_wallet()
@ -5473,8 +5583,8 @@ pub(crate) mod test {
while sig_len < 71 { while sig_len < 71 {
// Changing the OP_RETURN data will make the signature change (but not the fee, until // Changing the OP_RETURN data will make the signature change (but not the fee, until
// data[0] is small enough) // data[0] is small enough)
data[0] += 1; let data: &PushBytes = From::<&[u8; 1]>::from(&[1; 1]);
psbt.unsigned_tx.output[op_return_vout].script_pubkey = Script::new_op_return(&data); psbt.unsigned_tx.output[op_return_vout].script_pubkey = ScriptBuf::new_op_return(&data);
// Clearing the previous signature // Clearing the previous signature
psbt.inputs[0].partial_sigs.clear(); psbt.inputs[0].partial_sigs.clear();
// Signing // Signing
@ -5545,7 +5655,6 @@ pub(crate) mod test {
#[test] #[test]
fn test_create_signer() { fn test_create_signer() {
use crate::wallet::hardwaresigner::HWISigner; use crate::wallet::hardwaresigner::HWISigner;
use hwi::types::HWIChain;
use hwi::HWIClient; use hwi::HWIClient;
let mut devices = HWIClient::enumerate().unwrap(); let mut devices = HWIClient::enumerate().unwrap();
@ -5553,9 +5662,9 @@ pub(crate) mod test {
panic!("No devices found!"); panic!("No devices found!");
} }
let device = devices.remove(0).unwrap(); let device = devices.remove(0).unwrap();
let client = HWIClient::get_client(&device, true, HWIChain::Regtest).unwrap(); let client = HWIClient::get_client(&device, true, Network::Regtest.into()).unwrap();
let descriptors = client.get_descriptors::<String>(None).unwrap(); let descriptors = client.get_descriptors::<String>(None).unwrap();
let custom_signer = HWISigner::from_device(&device, HWIChain::Regtest).unwrap(); let custom_signer = HWISigner::from_device(&device, Network::Regtest.into()).unwrap();
let (mut wallet, _, _) = get_funded_wallet(&descriptors.internal[0]); let (mut wallet, _, _) = get_funded_wallet(&descriptors.internal[0]);
wallet.add_signer( wallet.add_signer(

View File

@ -19,7 +19,7 @@
//! # use std::str::FromStr; //! # use std::str::FromStr;
//! # use bitcoin::secp256k1::{Secp256k1, All}; //! # use bitcoin::secp256k1::{Secp256k1, All};
//! # use bitcoin::*; //! # use bitcoin::*;
//! # use bitcoin::util::psbt; //! # use bitcoin::psbt;
//! # use bdk::signer::*; //! # use bdk::signer::*;
//! # use bdk::database::*; //! # use bdk::database::*;
//! # use bdk::*; //! # use bdk::*;
@ -86,18 +86,17 @@ use std::fmt;
use std::ops::{Bound::Included, Deref}; use std::ops::{Bound::Included, Deref};
use std::sync::Arc; use std::sync::Arc;
use bitcoin::blockdata::opcodes; use bitcoin::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint};
use bitcoin::blockdata::script::Builder as ScriptBuilder; use bitcoin::hashes::hash160;
use bitcoin::hashes::{hash160, Hash};
use bitcoin::secp256k1::Message; use bitcoin::secp256k1::Message;
use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPrivKey, Fingerprint}; use bitcoin::sighash::{EcdsaSighashType, TapSighash, TapSighashType};
use bitcoin::util::{ecdsa, psbt, schnorr, sighash, taproot}; use bitcoin::{ecdsa, psbt, sighash, taproot};
use bitcoin::{secp256k1, XOnlyPublicKey}; use bitcoin::{key::TapTweak, key::XOnlyPublicKey, secp256k1};
use bitcoin::{EcdsaSighashType, PrivateKey, PublicKey, SchnorrSighashType, Script}; use bitcoin::{PrivateKey, PublicKey};
use miniscript::descriptor::{ use miniscript::descriptor::{
Descriptor, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, KeyMap, SinglePriv, Descriptor, DescriptorMultiXKey, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey,
SinglePubKey, InnerXKey, KeyMap, SinglePriv, SinglePubKey,
}; };
use miniscript::{Legacy, Segwitv0, SigType, Tap, ToPublicKey}; use miniscript::{Legacy, Segwitv0, SigType, Tap, ToPublicKey};
@ -130,7 +129,7 @@ impl From<Fingerprint> for SignerId {
} }
/// Signing error /// Signing error
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug)]
pub enum SignerError { pub enum SignerError {
/// The private key is missing for the required public key /// The private key is missing for the required public key
MissingKey, MissingKey,
@ -382,6 +381,49 @@ impl InputSigner for SignerWrapper<DescriptorXKey<ExtendedPrivKey>> {
} }
} }
fn multikey_to_xkeys<K: InnerXKey + Clone>(
multikey: DescriptorMultiXKey<K>,
) -> Vec<DescriptorXKey<K>> {
multikey
.derivation_paths
.clone()
.into_paths()
.into_iter()
.map(|derivation_path| DescriptorXKey {
origin: multikey.origin.clone(),
xkey: multikey.xkey.clone(),
derivation_path,
wildcard: multikey.wildcard,
})
.collect()
}
impl SignerCommon for SignerWrapper<DescriptorMultiXKey<ExtendedPrivKey>> {
fn id(&self, secp: &SecpCtx) -> SignerId {
SignerId::from(self.root_fingerprint(secp))
}
fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> {
Some(DescriptorSecretKey::MultiXPrv(self.signer.clone()))
}
}
impl InputSigner for SignerWrapper<DescriptorMultiXKey<ExtendedPrivKey>> {
fn sign_input(
&self,
psbt: &mut psbt::PartiallySignedTransaction,
input_index: usize,
sign_options: &SignOptions,
secp: &SecpCtx,
) -> Result<(), SignerError> {
let xkeys = multikey_to_xkeys(self.signer.clone());
for xkey in xkeys {
SignerWrapper::new(xkey, self.ctx).sign_input(psbt, input_index, sign_options, secp)?
}
Ok(())
}
}
impl SignerCommon for SignerWrapper<PrivateKey> { impl SignerCommon for SignerWrapper<PrivateKey> {
fn id(&self, secp: &SecpCtx) -> SignerId { fn id(&self, secp: &SecpCtx) -> SignerId {
SignerId::from(self.public_key(secp).to_pubkeyhash(SigType::Ecdsa)) SignerId::from(self.public_key(secp).to_pubkeyhash(SigType::Ecdsa))
@ -476,8 +518,16 @@ impl InputSigner for SignerWrapper<PrivateKey> {
} }
let (hash, hash_ty) = match self.ctx { let (hash, hash_ty) = match self.ctx {
SignerContext::Segwitv0 => Segwitv0::sighash(psbt, input_index, ())?, SignerContext::Segwitv0 => {
SignerContext::Legacy => Legacy::sighash(psbt, input_index, ())?, let (h, t) = Segwitv0::sighash(psbt, input_index, ())?;
let h = h.to_raw_hash();
(h, t)
}
SignerContext::Legacy => {
let (h, t) = Legacy::sighash(psbt, input_index, ())?;
let h = h.to_raw_hash();
(h, t)
}
_ => return Ok(()), // handled above _ => return Ok(()), // handled above
}; };
sign_psbt_ecdsa( sign_psbt_ecdsa(
@ -498,12 +548,12 @@ fn sign_psbt_ecdsa(
secret_key: &secp256k1::SecretKey, secret_key: &secp256k1::SecretKey,
pubkey: PublicKey, pubkey: PublicKey,
psbt_input: &mut psbt::Input, psbt_input: &mut psbt::Input,
hash: bitcoin::Sighash, hash: impl bitcoin::hashes::Hash + bitcoin::secp256k1::ThirtyTwoByteHash,
hash_ty: EcdsaSighashType, hash_ty: EcdsaSighashType,
secp: &SecpCtx, secp: &SecpCtx,
allow_grinding: bool, allow_grinding: bool,
) { ) {
let msg = &Message::from_slice(&hash.into_inner()[..]).unwrap(); let msg = &Message::from(hash);
let sig = if allow_grinding { let sig = if allow_grinding {
secp.sign_ecdsa_low_r(msg, secret_key) secp.sign_ecdsa_low_r(msg, secret_key)
} else { } else {
@ -512,7 +562,7 @@ fn sign_psbt_ecdsa(
secp.verify_ecdsa(msg, &sig, &pubkey.inner) secp.verify_ecdsa(msg, &sig, &pubkey.inner)
.expect("invalid or corrupted ecdsa signature"); .expect("invalid or corrupted ecdsa signature");
let final_signature = ecdsa::EcdsaSig { sig, hash_ty }; let final_signature = ecdsa::Signature { sig, hash_ty };
psbt_input.partial_sigs.insert(pubkey, final_signature); psbt_input.partial_sigs.insert(pubkey, final_signature);
} }
@ -522,12 +572,10 @@ fn sign_psbt_schnorr(
pubkey: XOnlyPublicKey, pubkey: XOnlyPublicKey,
leaf_hash: Option<taproot::TapLeafHash>, leaf_hash: Option<taproot::TapLeafHash>,
psbt_input: &mut psbt::Input, psbt_input: &mut psbt::Input,
hash: taproot::TapSighashHash, hash: TapSighash,
hash_ty: SchnorrSighashType, hash_ty: TapSighashType,
secp: &SecpCtx, secp: &SecpCtx,
) { ) {
use schnorr::TapTweak;
let keypair = secp256k1::KeyPair::from_seckey_slice(secp, secret_key.as_ref()).unwrap(); let keypair = secp256k1::KeyPair::from_seckey_slice(secp, secret_key.as_ref()).unwrap();
let keypair = match leaf_hash { let keypair = match leaf_hash {
None => keypair None => keypair
@ -536,12 +584,12 @@ fn sign_psbt_schnorr(
Some(_) => keypair, // no tweak for script spend Some(_) => keypair, // no tweak for script spend
}; };
let msg = &Message::from_slice(&hash.into_inner()[..]).unwrap(); let msg = &Message::from(hash);
let sig = secp.sign_schnorr(msg, &keypair); let sig = secp.sign_schnorr(msg, &keypair);
secp.verify_schnorr(&sig, msg, &XOnlyPublicKey::from_keypair(&keypair).0) secp.verify_schnorr(&sig, msg, &XOnlyPublicKey::from_keypair(&keypair).0)
.expect("invalid or corrupted schnorr signature"); .expect("invalid or corrupted schnorr signature");
let final_signature = schnorr::SchnorrSig { sig, hash_ty }; let final_signature = taproot::Signature { sig, hash_ty };
if let Some(lh) = leaf_hash { if let Some(lh) = leaf_hash {
psbt_input psbt_input
@ -631,6 +679,11 @@ impl SignersContainer {
SignerOrdering::default(), SignerOrdering::default(),
Arc::new(SignerWrapper::new(xprv, ctx)), Arc::new(SignerWrapper::new(xprv, ctx)),
), ),
DescriptorSecretKey::MultiXPrv(xprv) => container.add_external(
SignerId::from(xprv.root_fingerprint(secp)),
SignerOrdering::default(),
Arc::new(SignerWrapper::new(xprv, ctx)),
),
}; };
} }
@ -801,7 +854,7 @@ pub(crate) trait ComputeSighash {
impl ComputeSighash for Legacy { impl ComputeSighash for Legacy {
type Extra = (); type Extra = ();
type Sighash = bitcoin::Sighash; type Sighash = sighash::LegacySighash;
type SighashType = EcdsaSighashType; type SighashType = EcdsaSighashType;
fn sighash( fn sighash(
@ -848,19 +901,9 @@ impl ComputeSighash for Legacy {
} }
} }
fn p2wpkh_script_code(script: &Script) -> Script {
ScriptBuilder::new()
.push_opcode(opcodes::all::OP_DUP)
.push_opcode(opcodes::all::OP_HASH160)
.push_slice(&script[2..])
.push_opcode(opcodes::all::OP_EQUALVERIFY)
.push_opcode(opcodes::all::OP_CHECKSIG)
.into_script()
}
impl ComputeSighash for Segwitv0 { impl ComputeSighash for Segwitv0 {
type Extra = (); type Extra = ();
type Sighash = bitcoin::Sighash; type Sighash = sighash::SegwitV0Sighash;
type SighashType = EcdsaSighashType; type SighashType = EcdsaSighashType;
fn sighash( fn sighash(
@ -907,14 +950,21 @@ impl ComputeSighash for Segwitv0 {
Some(ref witness_script) => witness_script.clone(), Some(ref witness_script) => witness_script.clone(),
None => { None => {
if utxo.script_pubkey.is_v0_p2wpkh() { if utxo.script_pubkey.is_v0_p2wpkh() {
p2wpkh_script_code(&utxo.script_pubkey) utxo.script_pubkey
.p2wpkh_script_code()
.expect("We check above that the spk is a p2wpkh")
} else if psbt_input } else if psbt_input
.redeem_script .redeem_script
.as_ref() .as_ref()
.map(Script::is_v0_p2wpkh) .map(|s| s.is_v0_p2wpkh())
.unwrap_or(false) .unwrap_or(false)
{ {
p2wpkh_script_code(psbt_input.redeem_script.as_ref().unwrap()) psbt_input
.redeem_script
.as_ref()
.unwrap()
.p2wpkh_script_code()
.expect("We check above that the spk is a p2wpkh")
} else { } else {
return Err(SignerError::MissingWitnessScript); return Err(SignerError::MissingWitnessScript);
} }
@ -935,14 +985,14 @@ impl ComputeSighash for Segwitv0 {
impl ComputeSighash for Tap { impl ComputeSighash for Tap {
type Extra = Option<taproot::TapLeafHash>; type Extra = Option<taproot::TapLeafHash>;
type Sighash = taproot::TapSighashHash; type Sighash = TapSighash;
type SighashType = SchnorrSighashType; type SighashType = TapSighashType;
fn sighash( fn sighash(
psbt: &psbt::PartiallySignedTransaction, psbt: &psbt::PartiallySignedTransaction,
input_index: usize, input_index: usize,
extra: Self::Extra, extra: Self::Extra,
) -> Result<(Self::Sighash, SchnorrSighashType), SignerError> { ) -> Result<(Self::Sighash, TapSighashType), SignerError> {
if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() { if input_index >= psbt.inputs.len() || input_index >= psbt.unsigned_tx.input.len() {
return Err(SignerError::InputIndexOutOfRange); return Err(SignerError::InputIndexOutOfRange);
} }
@ -951,8 +1001,8 @@ impl ComputeSighash for Tap {
let sighash_type = psbt_input let sighash_type = psbt_input
.sighash_type .sighash_type
.unwrap_or_else(|| SchnorrSighashType::Default.into()) .unwrap_or_else(|| TapSighashType::Default.into())
.schnorr_hash_ty() .taproot_hash_ty()
.map_err(|_| SignerError::InvalidSighash)?; .map_err(|_| SignerError::InvalidSighash)?;
let witness_utxos = (0..psbt.inputs.len()) let witness_utxos = (0..psbt.inputs.len())
.map(|i| psbt.get_utxo_for(i)) .map(|i| psbt.get_utxo_for(i))
@ -1014,8 +1064,8 @@ mod signers_container_tests {
use crate::descriptor::IntoWalletDescriptor; use crate::descriptor::IntoWalletDescriptor;
use crate::keys::{DescriptorKey, IntoDescriptorKey}; use crate::keys::{DescriptorKey, IntoDescriptorKey};
use assert_matches::assert_matches; use assert_matches::assert_matches;
use bitcoin::bip32;
use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::util::bip32;
use bitcoin::Network; use bitcoin::Network;
use miniscript::ScriptContext; use miniscript::ScriptContext;
use std::str::FromStr; use std::str::FromStr;

View File

@ -18,7 +18,7 @@
//! # use bitcoin::*; //! # use bitcoin::*;
//! # use bdk::*; //! # use bdk::*;
//! # use bdk::wallet::tx_builder::CreateTx; //! # use bdk::wallet::tx_builder::CreateTx;
//! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); //! # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
//! # let wallet = doctest_wallet!(); //! # let wallet = doctest_wallet!();
//! // create a TxBuilder from a wallet //! // create a TxBuilder from a wallet
//! let mut tx_builder = wallet.build_tx(); //! let mut tx_builder = wallet.build_tx();
@ -27,7 +27,7 @@
//! // Create a transaction with one output to `to_address` of 50_000 satoshi //! // Create a transaction with one output to `to_address` of 50_000 satoshi
//! .add_recipient(to_address.script_pubkey(), 50_000) //! .add_recipient(to_address.script_pubkey(), 50_000)
//! // With a custom fee rate of 5.0 satoshi/vbyte //! // With a custom fee rate of 5.0 satoshi/vbyte
//! .fee_rate(FeeRate::from_sat_per_vb(5.0)) //! .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0))
//! // Only spend non-change outputs //! // Only spend non-change outputs
//! .do_not_spend_change() //! .do_not_spend_change()
//! // Turn on RBF signaling //! // Turn on RBF signaling
@ -41,8 +41,8 @@ use std::collections::HashSet;
use std::default::Default; use std::default::Default;
use std::marker::PhantomData; use std::marker::PhantomData;
use bitcoin::util::psbt::{self, PartiallySignedTransaction as Psbt}; use bitcoin::psbt::{self, PartiallySignedTransaction as Psbt};
use bitcoin::{LockTime, OutPoint, Script, Sequence, Transaction}; use bitcoin::{absolute, script::PushBytes, OutPoint, ScriptBuf, Sequence, Transaction};
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm}; use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
use crate::{database::BatchDatabase, Error, Utxo, Wallet}; use crate::{database::BatchDatabase, Error, Utxo, Wallet};
@ -79,7 +79,7 @@ impl TxBuilderContext for BumpFee {}
/// # use bitcoin::*; /// # use bitcoin::*;
/// # use core::str::FromStr; /// # use core::str::FromStr;
/// # let wallet = doctest_wallet!(); /// # let wallet = doctest_wallet!();
/// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let addr1 = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap().assume_checked();
/// # let addr2 = addr1.clone(); /// # let addr2 = addr1.clone();
/// // chaining /// // chaining
/// let (psbt1, details) = { /// let (psbt1, details) = {
@ -126,9 +126,9 @@ pub struct TxBuilder<'a, D, Cs, Ctx> {
//TODO: TxParams should eventually be exposed publicly. //TODO: TxParams should eventually be exposed publicly.
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub(crate) struct TxParams { pub(crate) struct TxParams {
pub(crate) recipients: Vec<(Script, u64)>, pub(crate) recipients: Vec<(ScriptBuf, u64)>,
pub(crate) drain_wallet: bool, pub(crate) drain_wallet: bool,
pub(crate) drain_to: Option<Script>, pub(crate) drain_to: Option<ScriptBuf>,
pub(crate) fee_policy: Option<FeePolicy>, pub(crate) fee_policy: Option<FeePolicy>,
pub(crate) internal_policy_path: Option<BTreeMap<String, Vec<usize>>>, pub(crate) internal_policy_path: Option<BTreeMap<String, Vec<usize>>>,
pub(crate) external_policy_path: Option<BTreeMap<String, Vec<usize>>>, pub(crate) external_policy_path: Option<BTreeMap<String, Vec<usize>>>,
@ -137,7 +137,7 @@ pub(crate) struct TxParams {
pub(crate) manually_selected_only: bool, pub(crate) manually_selected_only: bool,
pub(crate) sighash: Option<psbt::PsbtSighashType>, pub(crate) sighash: Option<psbt::PsbtSighashType>,
pub(crate) ordering: TxOrdering, pub(crate) ordering: TxOrdering,
pub(crate) locktime: Option<LockTime>, pub(crate) locktime: Option<absolute::LockTime>,
pub(crate) rbf: Option<RbfValue>, pub(crate) rbf: Option<RbfValue>,
pub(crate) version: Option<Version>, pub(crate) version: Option<Version>,
pub(crate) change_policy: ChangeSpendPolicy, pub(crate) change_policy: ChangeSpendPolicy,
@ -145,7 +145,7 @@ pub(crate) struct TxParams {
pub(crate) add_global_xpubs: bool, pub(crate) add_global_xpubs: bool,
pub(crate) include_output_redeem_witness_script: bool, pub(crate) include_output_redeem_witness_script: bool,
pub(crate) bumping_fee: Option<PreviousFee>, pub(crate) bumping_fee: Option<PreviousFee>,
pub(crate) current_height: Option<LockTime>, pub(crate) current_height: Option<absolute::LockTime>,
pub(crate) allow_dust: bool, pub(crate) allow_dust: bool,
} }
@ -241,7 +241,10 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
/// # use std::collections::BTreeMap; /// # use std::collections::BTreeMap;
/// # use bitcoin::*; /// # use bitcoin::*;
/// # use bdk::*; /// # use bdk::*;
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address =
/// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
/// .unwrap()
/// .assume_checked();
/// # let wallet = doctest_wallet!(); /// # let wallet = doctest_wallet!();
/// let mut path = BTreeMap::new(); /// let mut path = BTreeMap::new();
/// path.insert("aabbccdd".to_string(), vec![0, 1]); /// path.insert("aabbccdd".to_string(), vec![0, 1]);
@ -281,6 +284,7 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
for utxo in utxos { for utxo in utxos {
let descriptor = self.wallet.get_descriptor_for_keychain(utxo.keychain); let descriptor = self.wallet.get_descriptor_for_keychain(utxo.keychain);
#[allow(deprecated)]
let satisfaction_weight = descriptor.max_satisfaction_weight().unwrap(); let satisfaction_weight = descriptor.max_satisfaction_weight().unwrap();
self.params.utxos.push(WeightedUtxo { self.params.utxos.push(WeightedUtxo {
satisfaction_weight, satisfaction_weight,
@ -424,7 +428,7 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
/// Use a specific nLockTime while creating the transaction /// Use a specific nLockTime while creating the transaction
/// ///
/// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator. /// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator.
pub fn nlocktime(&mut self, locktime: LockTime) -> &mut Self { pub fn nlocktime(&mut self, locktime: absolute::LockTime) -> &mut Self {
self.params.locktime = Some(locktime); self.params.locktime = Some(locktime);
self self
} }
@ -463,7 +467,7 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
self self
} }
/// Only Fill-in the [`psbt::Input::witness_utxo`](bitcoin::util::psbt::Input::witness_utxo) field when spending from /// Only Fill-in the [`psbt::Input::witness_utxo`](bitcoin::psbt::Input::witness_utxo) field when spending from
/// SegWit descriptors. /// SegWit descriptors.
/// ///
/// This reduces the size of the PSBT, but some signers might reject them due to the lack of /// This reduces the size of the PSBT, but some signers might reject them due to the lack of
@ -473,8 +477,8 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
self self
} }
/// Fill-in the [`psbt::Output::redeem_script`](bitcoin::util::psbt::Output::redeem_script) and /// Fill-in the [`psbt::Output::redeem_script`](bitcoin::psbt::Output::redeem_script) and
/// [`psbt::Output::witness_script`](bitcoin::util::psbt::Output::witness_script) fields. /// [`psbt::Output::witness_script`](bitcoin::psbt::Output::witness_script) fields.
/// ///
/// This is useful for signers which always require it, like ColdCard hardware wallets. /// This is useful for signers which always require it, like ColdCard hardware wallets.
pub fn include_output_redeem_witness_script(&mut self) -> &mut Self { pub fn include_output_redeem_witness_script(&mut self) -> &mut Self {
@ -556,7 +560,8 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
/// ///
/// In both cases, if you don't provide a current height, we use the last sync height. /// In both cases, if you don't provide a current height, we use the last sync height.
pub fn current_height(&mut self, height: u32) -> &mut Self { pub fn current_height(&mut self, height: u32) -> &mut Self {
self.params.current_height = Some(LockTime::from_height(height).expect("Invalid height")); self.params.current_height =
Some(absolute::LockTime::from_height(height).expect("Invalid height"));
self self
} }
@ -571,20 +576,20 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, D, Cs, CreateTx> { impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, D, Cs, CreateTx> {
/// Replace the recipients already added with a new list /// Replace the recipients already added with a new list
pub fn set_recipients(&mut self, recipients: Vec<(Script, u64)>) -> &mut Self { pub fn set_recipients(&mut self, recipients: Vec<(ScriptBuf, u64)>) -> &mut Self {
self.params.recipients = recipients; self.params.recipients = recipients;
self self
} }
/// Add a recipient to the internal list /// Add a recipient to the internal list
pub fn add_recipient(&mut self, script_pubkey: Script, amount: u64) -> &mut Self { pub fn add_recipient(&mut self, script_pubkey: ScriptBuf, amount: u64) -> &mut Self {
self.params.recipients.push((script_pubkey, amount)); self.params.recipients.push((script_pubkey, amount));
self self
} }
/// Add data as an output, using OP_RETURN /// Add data as an output, using OP_RETURN
pub fn add_data(&mut self, data: &[u8]) -> &mut Self { pub fn add_data<T: AsRef<PushBytes>>(&mut self, data: &T) -> &mut Self {
let script = Script::new_op_return(data); let script = ScriptBuf::new_op_return(data);
self.add_recipient(script, 0u64); self.add_recipient(script, 0u64);
self self
} }
@ -614,7 +619,10 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, D, Cs, C
/// # use bitcoin::*; /// # use bitcoin::*;
/// # use bdk::*; /// # use bdk::*;
/// # use bdk::wallet::tx_builder::CreateTx; /// # use bdk::wallet::tx_builder::CreateTx;
/// # let to_address = Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt").unwrap(); /// # let to_address =
/// Address::from_str("2N4eQYCbKUHCCTUjBJeHcJp9ok6J2GZsTDt")
/// .unwrap()
/// .assume_checked();
/// # let wallet = doctest_wallet!(); /// # let wallet = doctest_wallet!();
/// let mut tx_builder = wallet.build_tx(); /// let mut tx_builder = wallet.build_tx();
/// ///
@ -623,7 +631,7 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, D, Cs, C
/// .drain_wallet() /// .drain_wallet()
/// // Send the excess (which is all the coins minus the fee) to this address. /// // Send the excess (which is all the coins minus the fee) to this address.
/// .drain_to(to_address.script_pubkey()) /// .drain_to(to_address.script_pubkey())
/// .fee_rate(FeeRate::from_sat_per_vb(5.0)) /// .fee_rate(bdk::FeeRate::from_sat_per_vb(5.0))
/// .enable_rbf(); /// .enable_rbf();
/// let (psbt, tx_details) = tx_builder.finish()?; /// let (psbt, tx_details) = tx_builder.finish()?;
/// # Ok::<(), bdk::Error>(()) /// # Ok::<(), bdk::Error>(())
@ -633,7 +641,7 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>> TxBuilder<'a, D, Cs, C
/// [`add_recipient`]: Self::add_recipient /// [`add_recipient`]: Self::add_recipient
/// [`add_utxos`]: Self::add_utxos /// [`add_utxos`]: Self::add_utxos
/// [`drain_wallet`]: Self::drain_wallet /// [`drain_wallet`]: Self::drain_wallet
pub fn drain_to(&mut self, script_pubkey: Script) -> &mut Self { pub fn drain_to(&mut self, script_pubkey: ScriptBuf) -> &mut Self {
self.params.drain_to = Some(script_pubkey); self.params.drain_to = Some(script_pubkey);
self self
} }
@ -651,7 +659,7 @@ impl<'a, D: BatchDatabase> TxBuilder<'a, D, DefaultCoinSelectionAlgorithm, BumpF
/// ///
/// Returns an `Err` if `script_pubkey` can't be found among the recipients of the /// Returns an `Err` if `script_pubkey` can't be found among the recipients of the
/// transaction we are bumping. /// transaction we are bumping.
pub fn allow_shrinking(&mut self, script_pubkey: Script) -> Result<&mut Self, Error> { pub fn allow_shrinking(&mut self, script_pubkey: ScriptBuf) -> Result<&mut Self, Error> {
match self match self
.params .params
.recipients .recipients
@ -790,6 +798,7 @@ mod test {
use bitcoin::consensus::deserialize; use bitcoin::consensus::deserialize;
use bitcoin::hashes::hex::FromHex; use bitcoin::hashes::hex::FromHex;
use std::str::FromStr;
use super::*; use super::*;
@ -821,8 +830,6 @@ mod test {
#[test] #[test]
fn test_output_ordering_bip69() { fn test_output_ordering_bip69() {
use std::str::FromStr;
let original_tx = ordering_test_tx!(); let original_tx = ordering_test_tx!();
let mut tx = original_tx; let mut tx = original_tx;
@ -851,8 +858,11 @@ mod test {
); );
assert_eq!(tx.output[0].value, 800); assert_eq!(tx.output[0].value, 800);
assert_eq!(tx.output[1].script_pubkey, From::from(vec![0xAA])); assert_eq!(tx.output[1].script_pubkey, ScriptBuf::from(vec![0xAA]));
assert_eq!(tx.output[2].script_pubkey, From::from(vec![0xAA, 0xEE])); assert_eq!(
tx.output[2].script_pubkey,
ScriptBuf::from(vec![0xAA, 0xEE])
);
} }
fn get_test_utxos() -> Vec<LocalUtxo> { fn get_test_utxos() -> Vec<LocalUtxo> {
@ -861,7 +871,7 @@ mod test {
vec![ vec![
LocalUtxo { LocalUtxo {
outpoint: OutPoint { outpoint: OutPoint {
txid: bitcoin::Txid::from_inner([0; 32]), txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(),
vout: 0, vout: 0,
}, },
txout: Default::default(), txout: Default::default(),
@ -870,7 +880,7 @@ mod test {
}, },
LocalUtxo { LocalUtxo {
outpoint: OutPoint { outpoint: OutPoint {
txid: bitcoin::Txid::from_inner([0; 32]), txid: bitcoin::Txid::from_slice(&[0; 32]).unwrap(),
vout: 1, vout: 1,
}, },
txout: Default::default(), txout: Default::default(),

View File

@ -10,7 +10,7 @@
// licenses. // licenses.
use bitcoin::secp256k1::{All, Secp256k1}; use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::{LockTime, Script, Sequence}; use bitcoin::{absolute, Script, Sequence};
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey}; use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
@ -65,7 +65,7 @@ pub(crate) fn check_nsequence_rbf(rbf: Sequence, csv: Sequence) -> bool {
} }
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After { impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
fn check_after(&self, n: LockTime) -> bool { fn check_after(&self, n: absolute::LockTime) -> bool {
if let Some(current_height) = self.current_height { if let Some(current_height) = self.current_height {
current_height >= n.to_consensus_u32() current_height >= n.to_consensus_u32()
} else { } else {
@ -114,17 +114,20 @@ pub(crate) type SecpCtx = Secp256k1<All>;
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::str::FromStr;
// When nSequence is lower than this flag the timelock is interpreted as block-height-based, // When nSequence is lower than this flag the timelock is interpreted as block-height-based,
// otherwise it's time-based // otherwise it's time-based
pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22; pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
use super::{check_nsequence_rbf, IsDust}; use super::{check_nsequence_rbf, IsDust};
use crate::bitcoin::{Address, Sequence}; use crate::bitcoin::{Address, Network, Sequence};
use std::str::FromStr;
#[test] #[test]
fn test_is_dust() { fn test_is_dust() {
let script_p2pkh = Address::from_str("1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe") let script_p2pkh = Address::from_str("1GNgwA8JfG7Kc8akJ8opdNWJUihqUztfPe")
.unwrap()
.require_network(Network::Bitcoin)
.unwrap() .unwrap()
.script_pubkey(); .script_pubkey();
assert!(script_p2pkh.is_p2pkh()); assert!(script_p2pkh.is_p2pkh());
@ -132,6 +135,8 @@ mod test {
assert!(!546.is_dust(&script_p2pkh)); assert!(!546.is_dust(&script_p2pkh));
let script_p2wpkh = Address::from_str("bc1qxlh2mnc0yqwas76gqq665qkggee5m98t8yskd8") let script_p2wpkh = Address::from_str("bc1qxlh2mnc0yqwas76gqq665qkggee5m98t8yskd8")
.unwrap()
.require_network(Network::Bitcoin)
.unwrap() .unwrap()
.script_pubkey(); .script_pubkey();
assert!(script_p2wpkh.is_v0_p2wpkh()); assert!(script_p2wpkh.is_v0_p2wpkh());