]> Untitled Git - bdk/commitdiff
Upgrade to rust-bitcoin 0.29
authorAlekos Filini <alekos.filini@gmail.com>
Tue, 25 Oct 2022 09:15:43 +0000 (11:15 +0200)
committerAlekos Filini <alekos.filini@gmail.com>
Tue, 25 Oct 2022 09:16:02 +0000 (11:16 +0200)
29 files changed:
Cargo.toml
README.md
examples/psbt_signer.rs
src/blockchain/any.rs
src/blockchain/compact_filters/mod.rs
src/blockchain/compact_filters/peer.rs
src/blockchain/compact_filters/store.rs
src/blockchain/compact_filters/sync.rs
src/blockchain/electrum.rs
src/blockchain/esplora/async.rs
src/blockchain/esplora/blocking.rs
src/blockchain/esplora/mod.rs
src/blockchain/rpc.rs
src/database/memory.rs
src/descriptor/derived.rs [deleted file]
src/descriptor/dsl.rs
src/descriptor/mod.rs
src/descriptor/policy.rs
src/descriptor/template.rs
src/error.rs
src/keys/mod.rs
src/testutils/blockchain_tests.rs
src/testutils/mod.rs
src/types.rs
src/wallet/coin_selection.rs
src/wallet/mod.rs
src/wallet/signer.rs
src/wallet/tx_builder.rs
src/wallet/utils.rs

index b3d7c67385177067485a64f25cec4049a9901841..6dcfead1fd347326fb997b1d98e9044d3b418e93 100644 (file)
@@ -14,16 +14,16 @@ license = "MIT OR Apache-2.0"
 [dependencies]
 bdk-macros = "^0.6"
 log = "^0.4"
-miniscript = { version = "7.0", features = ["use-serde"] }
-bitcoin = { version = "0.28.1", features = ["use-serde", "base64", "rand"] }
+miniscript = { version = "8.0", features = ["serde"] }
+bitcoin = { version = "0.29.1", features = ["serde", "base64", "rand"] }
 serde = { version = "^1.0", features = ["derive"] }
 serde_json = { version = "^1.0" }
 rand = "^0.8"
 
 # Optional dependencies
 sled = { version = "0.34", optional = true }
-electrum-client = { version = "0.11", optional = true }
-esplora-client = { version = "0.1.1", default-features = false, optional = true }
+electrum-client = { version = "0.12", optional = true }
+esplora-client = { version = "0.2", default-features = false, optional = true }
 rusqlite = { version = "0.27.0", optional = true }
 ahash = { version = "0.7.6", 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 }
 cc = { version = ">=1.0.64", optional = true }
 socks = { version = "0.3", optional = true }
-hwi = { version = "0.2.3", optional = true }
+hwi = { version = "0.3.0", optional = true }
 
 bip39 = { version = "1.0.1", optional = true }
 bitcoinconsensus = { version = "0.19.0-3", optional = true }
 
 # Needed by bdk_blockchain_tests macro and the `rpc` feature
-bitcoincore-rpc = { version = "0.15", optional = true }
+bitcoincore-rpc = { version = "0.16", optional = true }
 
 # Platform-specific dependencies
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
@@ -106,7 +106,9 @@ dev-getrandom-wasm = ["getrandom/js"]
 [dev-dependencies]
 lazy_static = "1.4"
 env_logger = "0.7"
-electrsd = "0.20"
+electrsd = "0.21"
+# Move back to importing from rust-bitcoin once https://github.com/rust-bitcoin/rust-bitcoin/pull/1342 is released
+base64 = "^0.13"
 
 [[example]]
 name = "compact_filters_balance"
index e7d5188cb75b8aaa8fe1f9d70c526b558823095d..d3ff3dc9ec0849c26ca8186b75b4ebcf66253e29 100644 (file)
--- a/README.md
+++ b/README.md
@@ -95,7 +95,7 @@ use bdk::blockchain::ElectrumBlockchain;
 use bdk::electrum_client::Client;
 use bdk::wallet::AddressIndex::New;
 
-use bitcoin::base64;
+use base64;
 use bitcoin::consensus::serialize;
 
 fn main() -> Result<(), bdk::Error> {
@@ -132,7 +132,7 @@ fn main() -> Result<(), bdk::Error> {
 ```rust,no_run
 use bdk::{Wallet, SignOptions, database::MemoryDatabase};
 
-use bitcoin::base64;
+use base64;
 use bitcoin::consensus::deserialize;
 
 fn main() -> Result<(), bdk::Error> {
index 8d6e96fdf03598e32e293c57de605e1db596eba1..35c539dad511a69a023780fc56ab8f779a40de2a 100644 (file)
@@ -33,8 +33,8 @@ fn main() -> Result<(), Box<dyn Error>> {
     let internal_secret_xkey = DescriptorSecretKey::from_str("[e9824965/84'/1'/0']tprv8fvem7qWxY3SGCQczQpRpqTKg455wf1zgixn6MZ4ze8gRfHjov5gXBQTadNfDgqs9ERbZZ3Bi1PNYrCCusFLucT39K525MWLpeURjHwUsfX/1/*").unwrap();
 
     let secp = Secp256k1::new();
-    let external_public_xkey = external_secret_xkey.as_public(&secp).unwrap();
-    let internal_public_xkey = internal_secret_xkey.as_public(&secp).unwrap();
+    let external_public_xkey = external_secret_xkey.to_public(&secp).unwrap();
+    let internal_public_xkey = internal_secret_xkey.to_public(&secp).unwrap();
 
     let signing_external_descriptor = descriptor!(wpkh(external_secret_xkey)).unwrap();
     let signing_internal_descriptor = descriptor!(wpkh(internal_secret_xkey)).unwrap();
index 5ef1a338514e393383bb196c84dff749da2af056..3138d02535c7ad6ea861c0c1a32ed555e5edd508 100644 (file)
@@ -194,7 +194,7 @@ impl_from!(boxed rpc::RpcBlockchain, AnyBlockchain, Rpc, #[cfg(feature = "rpc")]
 /// );
 /// # }
 /// ```
-#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq)]
+#[derive(Debug, serde::Serialize, serde::Deserialize, Clone, PartialEq, Eq)]
 #[serde(tag = "type", rename_all = "snake_case")]
 pub enum AnyBlockchainConfig {
     #[cfg(feature = "electrum")]
index 7ca78a2c34250cf44dc6161e61d4571a5847d185..9b47df9cf0c8bf064ffd992669ab8f07673940be 100644 (file)
@@ -479,7 +479,7 @@ impl WalletSync for CompactFiltersBlockchain {
 }
 
 /// Data to connect to a Bitcoin P2P peer
-#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq)]
+#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq)]
 pub struct BitcoinPeerConfig {
     /// Peer address such as 127.0.0.1:18333
     pub address: String,
@@ -490,7 +490,7 @@ pub struct BitcoinPeerConfig {
 }
 
 /// Configuration for a [`CompactFiltersBlockchain`]
-#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq)]
+#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq)]
 pub struct CompactFiltersBlockchainConfig {
     /// List of peers to try to connect to for asking headers and filters
     pub peers: Vec<BitcoinPeerConfig>,
index 413ea16977c99aa2b4e88263300e1c94d2a3557f..665a033d4be951253a6e3e6026e2ebe56bcfe89a 100644 (file)
@@ -75,7 +75,10 @@ impl Mempool {
     /// Look-up a transaction in the mempool given an [`Inventory`] request
     pub fn get_tx(&self, inventory: &Inventory) -> Option<Transaction> {
         let identifer = match inventory {
-            Inventory::Error | Inventory::Block(_) | Inventory::WitnessBlock(_) => return None,
+            Inventory::Error
+            | Inventory::Block(_)
+            | Inventory::WitnessBlock(_)
+            | Inventory::CompactBlock(_) => return None,
             Inventory::Transaction(txid) => TxIdentifier::Txid(*txid),
             Inventory::WitnessTransaction(txid) => TxIdentifier::Txid(*txid),
             Inventory::WTx(wtxid) => TxIdentifier::Wtxid(*wtxid),
index bb42a9c073827590d199da3752b42451ebcb2628..9d5731009dc84975a301f3df88e378eebb791d59 100644 (file)
@@ -103,42 +103,42 @@ where
 }
 
 impl Encodable for BundleStatus {
-    fn consensus_encode<W: Write>(&self, mut e: W) -> Result<usize, std::io::Error> {
+    fn consensus_encode<W: Write + ?Sized>(&self, e: &mut W) -> Result<usize, std::io::Error> {
         let mut written = 0;
 
         match self {
             BundleStatus::Init => {
-                written += 0x00u8.consensus_encode(&mut e)?;
+                written += 0x00u8.consensus_encode(e)?;
             }
             BundleStatus::CfHeaders { cf_headers } => {
-                written += 0x01u8.consensus_encode(&mut e)?;
-                written += VarInt(cf_headers.len() as u64).consensus_encode(&mut e)?;
+                written += 0x01u8.consensus_encode(e)?;
+                written += VarInt(cf_headers.len() as u64).consensus_encode(e)?;
                 for header in cf_headers {
-                    written += header.consensus_encode(&mut e)?;
+                    written += header.consensus_encode(e)?;
                 }
             }
             BundleStatus::CFilters { cf_filters } => {
-                written += 0x02u8.consensus_encode(&mut e)?;
-                written += VarInt(cf_filters.len() as u64).consensus_encode(&mut e)?;
+                written += 0x02u8.consensus_encode(e)?;
+                written += VarInt(cf_filters.len() as u64).consensus_encode(e)?;
                 for filter in cf_filters {
-                    written += filter.consensus_encode(&mut e)?;
+                    written += filter.consensus_encode(e)?;
                 }
             }
             BundleStatus::Processed { cf_filters } => {
-                written += 0x03u8.consensus_encode(&mut e)?;
-                written += VarInt(cf_filters.len() as u64).consensus_encode(&mut e)?;
+                written += 0x03u8.consensus_encode(e)?;
+                written += VarInt(cf_filters.len() as u64).consensus_encode(e)?;
                 for filter in cf_filters {
-                    written += filter.consensus_encode(&mut e)?;
+                    written += filter.consensus_encode(e)?;
                 }
             }
             BundleStatus::Pruned => {
-                written += 0x04u8.consensus_encode(&mut e)?;
+                written += 0x04u8.consensus_encode(e)?;
             }
             BundleStatus::Tip { cf_filters } => {
-                written += 0x05u8.consensus_encode(&mut e)?;
-                written += VarInt(cf_filters.len() as u64).consensus_encode(&mut e)?;
+                written += 0x05u8.consensus_encode(e)?;
+                written += VarInt(cf_filters.len() as u64).consensus_encode(e)?;
                 for filter in cf_filters {
-                    written += filter.consensus_encode(&mut e)?;
+                    written += filter.consensus_encode(e)?;
                 }
             }
         }
@@ -148,51 +148,53 @@ impl Encodable for BundleStatus {
 }
 
 impl Decodable for BundleStatus {
-    fn consensus_decode<D: Read>(mut d: D) -> Result<Self, bitcoin::consensus::encode::Error> {
-        let byte_type = u8::consensus_decode(&mut d)?;
+    fn consensus_decode<D: Read + ?Sized>(
+        d: &mut D,
+    ) -> Result<Self, bitcoin::consensus::encode::Error> {
+        let byte_type = u8::consensus_decode(d)?;
         match byte_type {
             0x00 => Ok(BundleStatus::Init),
             0x01 => {
-                let num = VarInt::consensus_decode(&mut d)?;
+                let num = VarInt::consensus_decode(d)?;
                 let num = num.0 as usize;
 
                 let mut cf_headers = Vec::with_capacity(num);
                 for _ in 0..num {
-                    cf_headers.push(FilterHeader::consensus_decode(&mut d)?);
+                    cf_headers.push(FilterHeader::consensus_decode(d)?);
                 }
 
                 Ok(BundleStatus::CfHeaders { cf_headers })
             }
             0x02 => {
-                let num = VarInt::consensus_decode(&mut d)?;
+                let num = VarInt::consensus_decode(d)?;
                 let num = num.0 as usize;
 
                 let mut cf_filters = Vec::with_capacity(num);
                 for _ in 0..num {
-                    cf_filters.push(Vec::<u8>::consensus_decode(&mut d)?);
+                    cf_filters.push(Vec::<u8>::consensus_decode(d)?);
                 }
 
                 Ok(BundleStatus::CFilters { cf_filters })
             }
             0x03 => {
-                let num = VarInt::consensus_decode(&mut d)?;
+                let num = VarInt::consensus_decode(d)?;
                 let num = num.0 as usize;
 
                 let mut cf_filters = Vec::with_capacity(num);
                 for _ in 0..num {
-                    cf_filters.push(Vec::<u8>::consensus_decode(&mut d)?);
+                    cf_filters.push(Vec::<u8>::consensus_decode(d)?);
                 }
 
                 Ok(BundleStatus::Processed { cf_filters })
             }
             0x04 => Ok(BundleStatus::Pruned),
             0x05 => {
-                let num = VarInt::consensus_decode(&mut d)?;
+                let num = VarInt::consensus_decode(d)?;
                 let num = num.0 as usize;
 
                 let mut cf_filters = Vec::with_capacity(num);
                 for _ in 0..num {
-                    cf_filters.push(Vec::<u8>::consensus_decode(&mut d)?);
+                    cf_filters.push(Vec::<u8>::consensus_decode(d)?);
                 }
 
                 Ok(BundleStatus::Tip { cf_filters })
@@ -276,7 +278,11 @@ impl ChainStore<Full> {
     }
 
     pub fn start_snapshot(&self, from: usize) -> Result<ChainStore<Snapshot>, CompactFiltersError> {
-        let new_cf_name: String = thread_rng().sample_iter(&Alphanumeric).take(16).collect();
+        let new_cf_name: String = thread_rng()
+            .sample_iter(&Alphanumeric)
+            .map(|byte| byte as char)
+            .take(16)
+            .collect();
         let new_cf_name = format!("_headers:{}", new_cf_name);
 
         let mut write_store = self.store.write().unwrap();
@@ -647,7 +653,7 @@ impl CfStore {
                     &first_key,
                     (
                         BundleStatus::Init,
-                        filter.filter_header(&FilterHeader::from_hash(Default::default())),
+                        filter.filter_header(&FilterHeader::from_hash(Hash::all_zeros())),
                     )
                         .serialize(),
                 )?;
index b12268dd246a519e170cfc3ce6d00c505155a823..a67b5705f680d1cdef38103de7bee27edc375aab 100644 (file)
@@ -14,6 +14,7 @@ use std::sync::{Arc, Mutex};
 use std::time::Duration;
 
 use bitcoin::hash_types::{BlockHash, FilterHeader};
+use bitcoin::hashes::Hash;
 use bitcoin::network::message::NetworkMessage;
 use bitcoin::network::message_blockdata::GetHeadersMessage;
 use bitcoin::util::bip158::BlockFilter;
@@ -254,7 +255,7 @@ where
 
     peer.send(NetworkMessage::GetHeaders(GetHeadersMessage::new(
         locators_vec,
-        Default::default(),
+        Hash::all_zeros(),
     )))?;
     let (mut snapshot, mut last_hash) = if let NetworkMessage::Headers(headers) = peer
         .recv("headers", Some(Duration::from_secs(TIMEOUT_SECS)))?
@@ -276,7 +277,7 @@ where
     while sync_height < peer.get_version().start_height as usize {
         peer.send(NetworkMessage::GetHeaders(GetHeadersMessage::new(
             vec![last_hash],
-            Default::default(),
+            Hash::all_zeros(),
         )))?;
         if let NetworkMessage::Headers(headers) = peer
             .recv("headers", Some(Duration::from_secs(TIMEOUT_SECS)))?
index fdb10b470f2a60bdd28a27154cefed10da982e0d..54381241ad2ec91ba3fe851458b2a4673db3bf43 100644 (file)
@@ -296,7 +296,7 @@ impl<'a, 'b, D: Database> TxCache<'a, 'b, D> {
 }
 
 /// Configuration for an [`ElectrumBlockchain`]
-#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq)]
+#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq)]
 pub struct ElectrumBlockchainConfig {
     /// URL of the Electrum server (such as ElectrumX, Esplora, BWT) may start with `ssl://` or `tcp://` and include a port
     ///
index 5ddbdeb41f7d8be57f133a2d68fe494447874635..900d95376dbe6b852af4cbab64309c046134d4e8 100644 (file)
@@ -125,8 +125,9 @@ impl GetTx for EsploraBlockchain {
 #[maybe_async]
 impl GetBlockHash for EsploraBlockchain {
     fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error> {
-        let block_header = await_or_block!(self.url_client.get_header(height as u32))?;
-        Ok(block_header.block_hash())
+        Ok(await_or_block!(self
+            .url_client
+            .get_block_hash(height as u32))?)
     }
 }
 
index 1e9d1cfcd7abd00978c4d2165fe86140d70f711b..768573c3f5b71f83f606a5a1dc2e8d58a90f3df8 100644 (file)
@@ -110,8 +110,7 @@ impl GetTx for EsploraBlockchain {
 
 impl GetBlockHash for EsploraBlockchain {
     fn get_block_hash(&self, height: u64) -> Result<BlockHash, Error> {
-        let block_header = self.url_client.get_header(height as u32)?;
-        Ok(block_header.block_hash())
+        Ok(self.url_client.get_block_hash(height as u32)?)
     }
 }
 
index 57032e49d30396f7f200da0846bdf64ef71539fa..c4308406b06d6e71c42f9cff2746f37d66079485 100644 (file)
@@ -33,7 +33,7 @@ mod blocking;
 pub use self::blocking::*;
 
 /// Configuration for an [`EsploraBlockchain`]
-#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq)]
+#[derive(Debug, serde::Deserialize, serde::Serialize, Clone, PartialEq, Eq)]
 pub struct EsploraBlockchainConfig {
     /// Base URL of the esplora service
     ///
index b2c64ba5ad4210d423fea3449f12586046bb7d53..d6a74d9c38f6792c1da955e9d49c65e168aada8b 100644 (file)
@@ -77,7 +77,7 @@ impl Deref for RpcBlockchain {
 }
 
 /// RpcBlockchain configuration options
-#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
 pub struct RpcConfig {
     /// The bitcoin node url
     pub url: String,
@@ -96,7 +96,7 @@ pub struct RpcConfig {
 /// In general, BDK tries to sync `scriptPubKey`s cached in [`crate::database::Database`] with
 /// `scriptPubKey`s imported in the Bitcoin Core Wallet. These parameters are used for determining
 /// how the `importdescriptors` RPC calls are to be made.
-#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
 pub struct RpcSyncParams {
     /// The minimum number of scripts to scan for on initial sync.
     pub start_script_count: usize,
@@ -167,7 +167,7 @@ impl Blockchain for RpcBlockchain {
             .estimate_smart_fee(target as u16, None)?
             .fee_rate
             .ok_or(Error::FeeRateUnavailable)?
-            .as_sat() as f64;
+            .to_sat() as f64;
 
         Ok(FeeRate::from_sat_per_vb((sat_per_kb / 1000f64) as f32))
     }
@@ -410,7 +410,12 @@ impl<'a, D: BatchDatabase> DbState<'a, D> {
                 updated = true;
                 TransactionDetails {
                     txid: tx_res.info.txid,
-                    ..Default::default()
+                    transaction: None,
+
+                    received: 0,
+                    sent: 0,
+                    fee: None,
+                    confirmation_time: None,
                 }
             });
 
@@ -430,7 +435,7 @@ impl<'a, D: BatchDatabase> DbState<'a, D> {
             // update fee (if needed)
             if let (None, Some(new_fee)) = (db_tx.fee, tx_res.detail.fee) {
                 updated = true;
-                db_tx.fee = Some(new_fee.as_sat().unsigned_abs());
+                db_tx.fee = Some(new_fee.to_sat().unsigned_abs());
             }
 
             // update confirmation time (if needed)
@@ -603,7 +608,7 @@ impl<'a, D: BatchDatabase> DbState<'a, D> {
         LocalUtxo {
             outpoint: OutPoint::new(entry.txid, entry.vout),
             txout: TxOut {
-                value: entry.amount.as_sat(),
+                value: entry.amount.to_sat(),
                 script_pubkey: entry.script_pub_key,
             },
             keychain,
@@ -873,15 +878,13 @@ impl BlockchainFactory for RpcBlockchainFactory {
 mod test {
     use super::*;
     use crate::{
-        descriptor::{into_wallet_descriptor_checked, AsDerived},
-        testutils::blockchain_tests::TestClient,
+        descriptor::into_wallet_descriptor_checked, testutils::blockchain_tests::TestClient,
         wallet::utils::SecpCtx,
     };
 
     use bitcoin::{Address, Network};
     use bitcoincore_rpc::RpcApi;
     use log::LevelFilter;
-    use miniscript::DescriptorTrait;
 
     crate::bdk_blockchain_tests! {
         fn test_instance(test_client: &TestClient) -> RpcBlockchain {
@@ -958,7 +961,7 @@ mod test {
 
         // generate scripts (1 tx per script)
         let scripts = (0..TX_COUNT)
-            .map(|index| desc.as_derived(index, &secp).script_pubkey())
+            .map(|index| desc.at_derivation_index(index).script_pubkey())
             .collect::<Vec<_>>();
 
         // import scripts and wait
index 691e7eb16b8ba1c5a6bdf94d887b8c0481c46330..6cfca6fc9b538ee77226c1f6a86baecad565e6d0 100644 (file)
@@ -497,7 +497,7 @@ macro_rules! populate_test_db {
         }
         let tx = $crate::bitcoin::Transaction {
             version: 1,
-            lock_time: 0,
+            lock_time: bitcoin::PackedLockTime(0),
             input,
             output: tx_meta
                 .output
diff --git a/src/descriptor/derived.rs b/src/descriptor/derived.rs
deleted file mode 100644 (file)
index 585c397..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-// Bitcoin Dev Kit
-// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
-//
-// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
-//
-// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
-// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
-// You may not use this file except in accordance with one or both of these
-// licenses.
-
-//! Derived descriptor keys
-//!
-//! The [`DerivedDescriptorKey`] type is a wrapper over the standard [`DescriptorPublicKey`] which
-//! guarantees that all the extended keys have a fixed derivation path, i.e. all the wildcards have
-//! been replaced by actual derivation indexes.
-//!
-//! The [`AsDerived`] trait provides a quick way to derive descriptors to obtain a
-//! `Descriptor<DerivedDescriptorKey>` type. This, in turn, can be used to derive public
-//! keys for arbitrary derivation indexes.
-//!
-//! Combining this with [`Wallet::get_signers`], secret keys can also be derived.
-//!
-//! # Example
-//!
-//! ```
-//! # use std::str::FromStr;
-//! # use bitcoin::secp256k1::Secp256k1;
-//! use bdk::descriptor::{AsDerived, DescriptorPublicKey};
-//! use bdk::miniscript::{ToPublicKey, TranslatePk, MiniscriptKey};
-//!
-//! let secp = Secp256k1::gen_new();
-//!
-//! let key = DescriptorPublicKey::from_str("[aa600a45/84'/0'/0']tpubDCbDXFKoLTQp44wQuC12JgSn5g9CWGjZdpBHeTqyypZ4VvgYjTJmK9CkyR5bFvG9f4PutvwmvpYCLkFx2rpx25hiMs4sUgxJveW8ZzSAVAc/0/*")?;
-//! let (descriptor, _, _) = bdk::descriptor!(wpkh(key))?;
-//!
-//! // derived: wpkh([aa600a45/84'/0'/0']tpubDCbDXFKoLTQp44wQuC12JgSn5g9CWGjZdpBHeTqyypZ4VvgYjTJmK9CkyR5bFvG9f4PutvwmvpYCLkFx2rpx25hiMs4sUgxJveW8ZzSAVAc/0/42)#3ladd0t2
-//! let derived = descriptor.as_derived(42, &secp);
-//! println!("derived: {}", derived);
-//!
-//! // with_pks: wpkh(02373ecb54c5e83bd7e0d40adf78b65efaf12fafb13571f0261fc90364eee22e1e)#p4jjgvll
-//! let with_pks = derived.translate_pk_infallible(|pk| pk.to_public_key(), |pkh| pkh.to_public_key().to_pubkeyhash());
-//! println!("with_pks: {}", with_pks);
-//! # Ok::<(), Box<dyn std::error::Error>>(())
-//! ```
-//!
-//! [`Wallet::get_signers`]: crate::wallet::Wallet::get_signers
-
-use std::cmp::Ordering;
-use std::fmt;
-use std::hash::{Hash, Hasher};
-use std::ops::Deref;
-
-use bitcoin::hashes::hash160;
-use bitcoin::{PublicKey, XOnlyPublicKey};
-
-use miniscript::descriptor::{DescriptorSinglePub, SinglePubKey, Wildcard};
-use miniscript::{Descriptor, DescriptorPublicKey, MiniscriptKey, ToPublicKey, TranslatePk};
-
-use crate::wallet::utils::SecpCtx;
-
-/// Extended [`DescriptorPublicKey`] that has been derived
-///
-/// Derived keys are guaranteed to never contain wildcards of any kind
-#[derive(Debug, Clone)]
-pub struct DerivedDescriptorKey<'s>(DescriptorPublicKey, &'s SecpCtx);
-
-impl<'s> DerivedDescriptorKey<'s> {
-    /// Construct a new derived key
-    ///
-    /// Panics if the key is wildcard
-    pub fn new(key: DescriptorPublicKey, secp: &'s SecpCtx) -> DerivedDescriptorKey<'s> {
-        if let DescriptorPublicKey::XPub(xpub) = &key {
-            assert!(xpub.wildcard == Wildcard::None)
-        }
-
-        DerivedDescriptorKey(key, secp)
-    }
-}
-
-impl<'s> Deref for DerivedDescriptorKey<'s> {
-    type Target = DescriptorPublicKey;
-
-    fn deref(&self) -> &Self::Target {
-        &self.0
-    }
-}
-
-impl<'s> PartialEq for DerivedDescriptorKey<'s> {
-    fn eq(&self, other: &Self) -> bool {
-        self.0 == other.0
-    }
-}
-
-impl<'s> Eq for DerivedDescriptorKey<'s> {}
-
-impl<'s> PartialOrd for DerivedDescriptorKey<'s> {
-    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
-        self.0.partial_cmp(&other.0)
-    }
-}
-
-impl<'s> Ord for DerivedDescriptorKey<'s> {
-    fn cmp(&self, other: &Self) -> Ordering {
-        self.0.cmp(&other.0)
-    }
-}
-
-impl<'s> fmt::Display for DerivedDescriptorKey<'s> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        self.0.fmt(f)
-    }
-}
-
-impl<'s> Hash for DerivedDescriptorKey<'s> {
-    fn hash<H: Hasher>(&self, state: &mut H) {
-        self.0.hash(state);
-    }
-}
-
-impl<'s> MiniscriptKey for DerivedDescriptorKey<'s> {
-    type Hash = Self;
-
-    fn to_pubkeyhash(&self) -> Self::Hash {
-        DerivedDescriptorKey(self.0.to_pubkeyhash(), self.1)
-    }
-
-    fn is_uncompressed(&self) -> bool {
-        self.0.is_uncompressed()
-    }
-}
-
-impl<'s> ToPublicKey for DerivedDescriptorKey<'s> {
-    fn to_public_key(&self) -> PublicKey {
-        match &self.0 {
-            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
-                key: SinglePubKey::XOnly(_),
-                ..
-            }) => panic!("Found x-only public key in non-tr descriptor"),
-            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
-                key: SinglePubKey::FullKey(ref pk),
-                ..
-            }) => *pk,
-            DescriptorPublicKey::XPub(ref xpub) => PublicKey::new(
-                xpub.xkey
-                    .derive_pub(self.1, &xpub.derivation_path)
-                    .expect("Shouldn't fail, only normal derivations")
-                    .public_key,
-            ),
-        }
-    }
-
-    fn to_x_only_pubkey(&self) -> XOnlyPublicKey {
-        match &self.0 {
-            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
-                key: SinglePubKey::XOnly(ref pk),
-                ..
-            }) => *pk,
-            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
-                key: SinglePubKey::FullKey(ref pk),
-                ..
-            }) => XOnlyPublicKey::from(pk.inner),
-            DescriptorPublicKey::XPub(ref xpub) => XOnlyPublicKey::from(
-                xpub.xkey
-                    .derive_pub(self.1, &xpub.derivation_path)
-                    .expect("Shouldn't fail, only normal derivations")
-                    .public_key,
-            ),
-        }
-    }
-
-    fn hash_to_hash160(hash: &Self::Hash) -> hash160::Hash {
-        hash.to_public_key().to_pubkeyhash()
-    }
-}
-
-/// Utilities to derive descriptors
-///
-/// Check out the [module level] documentation for more.
-///
-/// [module level]: crate::descriptor::derived
-pub trait AsDerived {
-    /// Derive a descriptor and transform all of its keys to `DerivedDescriptorKey`
-    fn as_derived<'s>(&self, index: u32, secp: &'s SecpCtx)
-        -> Descriptor<DerivedDescriptorKey<'s>>;
-
-    /// Transform the keys into `DerivedDescriptorKey`.
-    ///
-    /// Panics if the descriptor is not "fixed", i.e. if it's derivable
-    fn as_derived_fixed<'s>(&self, secp: &'s SecpCtx) -> Descriptor<DerivedDescriptorKey<'s>>;
-}
-
-impl AsDerived for Descriptor<DescriptorPublicKey> {
-    fn as_derived<'s>(
-        &self,
-        index: u32,
-        secp: &'s SecpCtx,
-    ) -> Descriptor<DerivedDescriptorKey<'s>> {
-        self.derive(index).translate_pk_infallible(
-            |key| DerivedDescriptorKey::new(key.clone(), secp),
-            |key| DerivedDescriptorKey::new(key.clone(), secp),
-        )
-    }
-
-    fn as_derived_fixed<'s>(&self, secp: &'s SecpCtx) -> Descriptor<DerivedDescriptorKey<'s>> {
-        assert!(!self.is_deriveable());
-
-        self.as_derived(0, secp)
-    }
-}
index 2d0d9422d4f4ef16b54fda9b49e45e561b683e14..67ef67057ffee7cbdf48e18030b7fbf72828b867 100644 (file)
@@ -700,10 +700,10 @@ macro_rules! fragment {
         $crate::keys::make_pkh($key, &secp)
     });
     ( after ( $value:expr ) ) => ({
-        $crate::impl_leaf_opcode_value!(After, $value)
+        $crate::impl_leaf_opcode_value!(After, $crate::bitcoin::PackedLockTime($value)) // TODO!! https://github.com/rust-bitcoin/rust-bitcoin/issues/1302
     });
     ( older ( $value:expr ) ) => ({
-        $crate::impl_leaf_opcode_value!(Older, $value)
+        $crate::impl_leaf_opcode_value!(Older, $crate::bitcoin::Sequence($value)) // TODO!!
     });
     ( sha256 ( $hash:expr ) ) => ({
         $crate::impl_leaf_opcode_value!(Sha256, $hash)
@@ -795,7 +795,7 @@ macro_rules! fragment {
 mod test {
     use bitcoin::hashes::hex::ToHex;
     use bitcoin::secp256k1::Secp256k1;
-    use miniscript::descriptor::{DescriptorPublicKey, DescriptorTrait, KeyMap};
+    use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
     use miniscript::{Descriptor, Legacy, Segwitv0};
 
     use std::str::FromStr;
@@ -806,8 +806,6 @@ mod test {
     use bitcoin::util::bip32;
     use bitcoin::PrivateKey;
 
-    use crate::descriptor::derived::AsDerived;
-
     // test the descriptor!() macro
 
     // verify descriptor generates expected script(s) (if bare or pk) or address(es)
@@ -817,17 +815,15 @@ mod test {
         is_fixed: bool,
         expected: &[&str],
     ) {
-        let secp = Secp256k1::new();
-
         let (desc, _key_map, _networks) = desc.unwrap();
         assert_eq!(desc.is_witness(), is_witness);
-        assert_eq!(!desc.is_deriveable(), is_fixed);
+        assert_eq!(!desc.has_wildcard(), is_fixed);
         for i in 0..expected.len() {
             let index = i as u32;
-            let child_desc = if !desc.is_deriveable() {
-                desc.as_derived_fixed(&secp)
+            let child_desc = if !desc.has_wildcard() {
+                desc.at_derivation_index(0)
             } else {
-                desc.as_derived(index, &secp)
+                desc.at_derivation_index(index)
             };
             let address = child_desc.address(Regtest);
             if let Ok(address) = address {
index 802ccd19ca2ababa877b4e1552058581eac2763b..aced91ca9a89c1814878c936ff7563025325a625 100644 (file)
 //! from [`miniscript`].
 
 use std::collections::BTreeMap;
-use std::ops::Deref;
 
 use bitcoin::util::bip32::{ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint, KeySource};
 use bitcoin::util::{psbt, taproot};
 use bitcoin::{secp256k1, PublicKey, XOnlyPublicKey};
-use bitcoin::{Network, Script, TxOut};
+use bitcoin::{Network, TxOut};
 
-use miniscript::descriptor::{DescriptorType, InnerXKey, SinglePubKey};
+use miniscript::descriptor::{DefiniteDescriptorKey, DescriptorType, InnerXKey, SinglePubKey};
 pub use miniscript::{
     descriptor::DescriptorXKey, descriptor::KeyMap, descriptor::Wildcard, Descriptor,
     DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
 };
-use miniscript::{DescriptorTrait, ForEachKey, TranslatePk};
+use miniscript::{ForEachKey, MiniscriptKey, TranslatePk};
 
 use crate::descriptor::policy::BuildSatisfaction;
 
 pub mod checksum;
-pub mod derived;
 #[doc(hidden)]
 pub mod dsl;
 pub mod error;
@@ -40,7 +38,6 @@ pub mod policy;
 pub mod template;
 
 pub use self::checksum::get_checksum;
-pub use self::derived::{AsDerived, DerivedDescriptorKey};
 pub use self::error::Error as DescriptorError;
 pub use self::policy::Policy;
 use self::template::DescriptorTemplateOut;
@@ -52,7 +49,7 @@ use crate::wallet::utils::SecpCtx;
 pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
 
 /// Alias for a [`Descriptor`] that contains extended **derived** keys
-pub type DerivedDescriptor<'s> = Descriptor<DerivedDescriptorKey<'s>>;
+pub type DerivedDescriptor = Descriptor<DefiniteDescriptorKey>;
 
 /// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or
 /// [`psbt::Output`]
@@ -132,28 +129,76 @@ impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
     ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
         use crate::keys::DescriptorKey;
 
-        let check_key = |pk: &DescriptorPublicKey| {
-            let (pk, _, networks) = if self.0.is_witness() {
-                let descriptor_key: DescriptorKey<miniscript::Segwitv0> =
-                    pk.clone().into_descriptor_key()?;
-                descriptor_key.extract(secp)?
-            } else {
-                let descriptor_key: DescriptorKey<miniscript::Legacy> =
-                    pk.clone().into_descriptor_key()?;
-                descriptor_key.extract(secp)?
-            };
-
-            if networks.contains(&network) {
-                Ok(pk)
-            } else {
-                Err(DescriptorError::Key(KeyError::InvalidNetwork))
+        struct Translator<'s, 'd> {
+            secp: &'s SecpCtx,
+            descriptor: &'d ExtendedDescriptor,
+            network: Network,
+        }
+
+        impl<'s, 'd>
+            miniscript::Translator<DescriptorPublicKey, miniscript::DummyKey, DescriptorError>
+            for Translator<'s, 'd>
+        {
+            fn pk(
+                &mut self,
+                pk: &DescriptorPublicKey,
+            ) -> Result<miniscript::DummyKey, DescriptorError> {
+                let secp = &self.secp;
+
+                let (_, _, networks) = if self.descriptor.is_taproot() {
+                    let descriptor_key: DescriptorKey<miniscript::Tap> =
+                        pk.clone().into_descriptor_key()?;
+                    descriptor_key.extract(secp)?
+                } else if self.descriptor.is_witness() {
+                    let descriptor_key: DescriptorKey<miniscript::Segwitv0> =
+                        pk.clone().into_descriptor_key()?;
+                    descriptor_key.extract(secp)?
+                } else {
+                    let descriptor_key: DescriptorKey<miniscript::Legacy> =
+                        pk.clone().into_descriptor_key()?;
+                    descriptor_key.extract(secp)?
+                };
+
+                if networks.contains(&self.network) {
+                    Ok(miniscript::DummyKey)
+                } else {
+                    Err(DescriptorError::Key(KeyError::InvalidNetwork))
+                }
             }
-        };
+            fn sha256(
+                &mut self,
+                _sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256,
+            ) -> Result<miniscript::DummySha256Hash, DescriptorError> {
+                Ok(Default::default())
+            }
+            fn hash256(
+                &mut self,
+                _hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256,
+            ) -> Result<miniscript::DummyHash256Hash, DescriptorError> {
+                Ok(Default::default())
+            }
+            fn ripemd160(
+                &mut self,
+                _ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160,
+            ) -> Result<miniscript::DummyRipemd160Hash, DescriptorError> {
+                Ok(Default::default())
+            }
+            fn hash160(
+                &mut self,
+                _hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160,
+            ) -> Result<miniscript::DummyHash160Hash, DescriptorError> {
+                Ok(Default::default())
+            }
+        }
 
         // check the network for the keys
-        let translated = self.0.translate_pk(check_key, check_key)?;
+        self.0.translate_pk(&mut Translator {
+            secp,
+            network,
+            descriptor: &self.0,
+        })?;
 
-        Ok((translated, self.1))
+        Ok(self)
     }
 }
 
@@ -163,10 +208,17 @@ impl IntoWalletDescriptor for DescriptorTemplateOut {
         _secp: &SecpCtx,
         network: Network,
     ) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
-        let valid_networks = &self.2;
+        struct Translator {
+            network: Network,
+        }
 
-        let fix_key = |pk: &DescriptorPublicKey| {
-            if valid_networks.contains(&network) {
+        impl miniscript::Translator<DescriptorPublicKey, DescriptorPublicKey, DescriptorError>
+            for Translator
+        {
+            fn pk(
+                &mut self,
+                pk: &DescriptorPublicKey,
+            ) -> Result<DescriptorPublicKey, DescriptorError> {
                 // workaround for xpubs generated by other key types, like bip39: since when the
                 // conversion is made one network has to be chosen, what we generally choose
                 // "mainnet", but then override the set of valid networks to specify that all of
@@ -175,7 +227,7 @@ impl IntoWalletDescriptor for DescriptorTemplateOut {
                 let pk = match pk {
                     DescriptorPublicKey::XPub(ref xpub) => {
                         let mut xpub = xpub.clone();
-                        xpub.xkey.network = network;
+                        xpub.xkey.network = self.network;
 
                         DescriptorPublicKey::XPub(xpub)
                     }
@@ -183,13 +235,20 @@ impl IntoWalletDescriptor for DescriptorTemplateOut {
                 };
 
                 Ok(pk)
-            } else {
-                Err(DescriptorError::Key(KeyError::InvalidNetwork))
             }
-        };
+            miniscript::translate_hash_clone!(
+                DescriptorPublicKey,
+                DescriptorPublicKey,
+                DescriptorError
+            );
+        }
+
+        if !self.2.contains(&network) {
+            return Err(DescriptorError::Key(KeyError::InvalidNetwork));
+        }
 
         // fixup the network for keys that need it
-        let translated = self.0.translate_pk(fix_key, fix_key)?;
+        let translated = self.0.translate_pk(&mut Translator { network })?;
 
         Ok((translated, self.1))
     }
@@ -210,7 +269,7 @@ pub(crate) fn into_wallet_descriptor_checked<T: IntoWalletDescriptor>(
             derivation_path,
             wildcard,
             ..
-        }) = k.as_key()
+        }) = k
         {
             return *wildcard == Wildcard::Hardened
                 || derivation_path.into_iter().any(ChildNumber::is_hardened);
@@ -294,11 +353,6 @@ where
     }
 }
 
-pub(crate) trait DerivedDescriptorMeta {
-    fn get_hd_keypaths(&self, secp: &SecpCtx) -> HdKeyPaths;
-    fn get_tap_key_origins(&self, secp: &SecpCtx) -> TapKeyOrigins;
-}
-
 pub(crate) trait DescriptorMeta {
     fn is_witness(&self) -> bool;
     fn is_taproot(&self) -> bool;
@@ -307,63 +361,23 @@ pub(crate) trait DescriptorMeta {
         &self,
         hd_keypaths: &HdKeyPaths,
         secp: &'s SecpCtx,
-    ) -> Option<DerivedDescriptor<'s>>;
+    ) -> Option<DerivedDescriptor>;
     fn derive_from_tap_key_origins<'s>(
         &self,
         tap_key_origins: &TapKeyOrigins,
         secp: &'s SecpCtx,
-    ) -> Option<DerivedDescriptor<'s>>;
+    ) -> Option<DerivedDescriptor>;
     fn derive_from_psbt_key_origins<'s>(
         &self,
         key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
         secp: &'s SecpCtx,
-    ) -> Option<DerivedDescriptor<'s>>;
+    ) -> Option<DerivedDescriptor>;
     fn derive_from_psbt_input<'s>(
         &self,
         psbt_input: &psbt::Input,
         utxo: Option<TxOut>,
         secp: &'s SecpCtx,
-    ) -> Option<DerivedDescriptor<'s>>;
-}
-
-pub(crate) trait DescriptorScripts {
-    fn psbt_redeem_script(&self) -> Option<Script>;
-    fn psbt_witness_script(&self) -> Option<Script>;
-}
-
-impl<'s> DescriptorScripts for DerivedDescriptor<'s> {
-    fn psbt_redeem_script(&self) -> Option<Script> {
-        match self.desc_type() {
-            DescriptorType::ShWpkh => Some(self.explicit_script().unwrap()),
-            DescriptorType::ShWsh => Some(self.explicit_script().unwrap().to_v0_p2wsh()),
-            DescriptorType::Sh => Some(self.explicit_script().unwrap()),
-            DescriptorType::Bare => Some(self.explicit_script().unwrap()),
-            DescriptorType::ShSortedMulti => Some(self.explicit_script().unwrap()),
-            DescriptorType::ShWshSortedMulti => Some(self.explicit_script().unwrap().to_v0_p2wsh()),
-            DescriptorType::Pkh
-            | DescriptorType::Wpkh
-            | DescriptorType::Tr
-            | DescriptorType::Wsh
-            | DescriptorType::WshSortedMulti => None,
-        }
-    }
-
-    fn psbt_witness_script(&self) -> Option<Script> {
-        match self.desc_type() {
-            DescriptorType::Wsh => Some(self.explicit_script().unwrap()),
-            DescriptorType::ShWsh => Some(self.explicit_script().unwrap()),
-            DescriptorType::WshSortedMulti | DescriptorType::ShWshSortedMulti => {
-                Some(self.explicit_script().unwrap())
-            }
-            DescriptorType::Bare
-            | DescriptorType::Sh
-            | DescriptorType::Pkh
-            | DescriptorType::Wpkh
-            | DescriptorType::ShSortedMulti
-            | DescriptorType::Tr
-            | DescriptorType::ShWpkh => None,
-        }
-    }
+    ) -> Option<DerivedDescriptor>;
 }
 
 impl DescriptorMeta for ExtendedDescriptor {
@@ -387,7 +401,7 @@ impl DescriptorMeta for ExtendedDescriptor {
         let mut answer = Vec::new();
 
         self.for_each_key(|pk| {
-            if let DescriptorPublicKey::XPub(xpub) = pk.as_key() {
+            if let DescriptorPublicKey::XPub(xpub) = pk {
                 answer.push(xpub.clone());
             }
 
@@ -401,7 +415,7 @@ impl DescriptorMeta for ExtendedDescriptor {
         &self,
         key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
         secp: &'s SecpCtx,
-    ) -> Option<DerivedDescriptor<'s>> {
+    ) -> Option<DerivedDescriptor> {
         // Ensure that deriving `xpub` with `path` yields `expected`
         let verify_key = |xpub: &DescriptorXKey<ExtendedPubKey>,
                           path: &DerivationPath,
@@ -423,7 +437,7 @@ impl DescriptorMeta for ExtendedDescriptor {
 
         // using `for_any_key` should make this stop as soon as we return `true`
         self.for_any_key(|key| {
-            if let DescriptorPublicKey::XPub(xpub) = key.as_key().deref() {
+            if let DescriptorPublicKey::XPub(xpub) = key {
                 // Check if the key matches one entry in our `key_origins`. If it does, `matches()` will
                 // return the "prefix" that matched, so we remove that prefix from the full path
                 // found in `key_origins` and save it in `derive_path`. We expect this to be a derivation
@@ -481,14 +495,14 @@ impl DescriptorMeta for ExtendedDescriptor {
             false
         });
 
-        path_found.map(|path| self.as_derived(path, secp))
+        path_found.map(|path| self.at_derivation_index(path))
     }
 
     fn derive_from_hd_keypaths<'s>(
         &self,
         hd_keypaths: &HdKeyPaths,
         secp: &'s SecpCtx,
-    ) -> Option<DerivedDescriptor<'s>> {
+    ) -> Option<DerivedDescriptor> {
         // "Convert" an hd_keypaths map to the format required by `derive_from_psbt_key_origins`
         let key_origins = hd_keypaths
             .iter()
@@ -506,7 +520,7 @@ impl DescriptorMeta for ExtendedDescriptor {
         &self,
         tap_key_origins: &TapKeyOrigins,
         secp: &'s SecpCtx,
-    ) -> Option<DerivedDescriptor<'s>> {
+    ) -> Option<DerivedDescriptor> {
         // "Convert" a tap_key_origins map to the format required by `derive_from_psbt_key_origins`
         let key_origins = tap_key_origins
             .iter()
@@ -520,19 +534,19 @@ impl DescriptorMeta for ExtendedDescriptor {
         psbt_input: &psbt::Input,
         utxo: Option<TxOut>,
         secp: &'s SecpCtx,
-    ) -> Option<DerivedDescriptor<'s>> {
+    ) -> Option<DerivedDescriptor> {
         if let Some(derived) = self.derive_from_hd_keypaths(&psbt_input.bip32_derivation, secp) {
             return Some(derived);
         }
         if let Some(derived) = self.derive_from_tap_key_origins(&psbt_input.tap_key_origins, secp) {
             return Some(derived);
         }
-        if self.is_deriveable() {
+        if self.has_wildcard() {
             // We can't try to bruteforce the derivation index, exit here
             return None;
         }
 
-        let descriptor = self.as_derived_fixed(secp);
+        let descriptor = self.at_derivation_index(0);
         match descriptor.desc_type() {
             // TODO: add pk() here
             DescriptorType::Pkh
@@ -566,86 +580,6 @@ impl DescriptorMeta for ExtendedDescriptor {
     }
 }
 
-impl<'s> DerivedDescriptorMeta for DerivedDescriptor<'s> {
-    fn get_hd_keypaths(&self, secp: &SecpCtx) -> HdKeyPaths {
-        let mut answer = BTreeMap::new();
-        self.for_each_key(|key| {
-            if let DescriptorPublicKey::XPub(xpub) = key.as_key().deref() {
-                let derived_pubkey = xpub
-                    .xkey
-                    .derive_pub(secp, &xpub.derivation_path)
-                    .expect("Derivation can't fail");
-
-                answer.insert(
-                    derived_pubkey.public_key,
-                    (xpub.root_fingerprint(secp), xpub.full_path(&[])),
-                );
-            }
-
-            true
-        });
-
-        answer
-    }
-
-    fn get_tap_key_origins(&self, secp: &SecpCtx) -> TapKeyOrigins {
-        use miniscript::ToPublicKey;
-
-        let mut answer = BTreeMap::new();
-        let mut insert_path = |pk: &DerivedDescriptorKey<'_>, lh| {
-            let key_origin = match pk.deref() {
-                DescriptorPublicKey::XPub(xpub) => {
-                    Some((xpub.root_fingerprint(secp), xpub.full_path(&[])))
-                }
-                DescriptorPublicKey::SinglePub(_) => None,
-            };
-
-            // If this is the internal key, we only insert the key origin if it's not None.
-            // For keys found in the tap tree we always insert a key origin (because the signer
-            // looks for it to know which leaves to sign for), even though it may be None
-            match (lh, key_origin) {
-                (None, Some(ko)) => {
-                    answer
-                        .entry(pk.to_x_only_pubkey())
-                        .or_insert_with(|| (vec![], ko));
-                }
-                (Some(lh), origin) => {
-                    answer
-                        .entry(pk.to_x_only_pubkey())
-                        .or_insert_with(|| (vec![], origin.unwrap_or_default()))
-                        .0
-                        .push(lh);
-                }
-                _ => {}
-            }
-        };
-
-        if let Descriptor::Tr(tr) = &self {
-            // Internal key first, then iterate the scripts
-            insert_path(tr.internal_key(), None);
-
-            for (_, ms) in tr.iter_scripts() {
-                // Assume always the same leaf version
-                let leaf_hash = taproot::TapLeafHash::from_script(
-                    &ms.encode(),
-                    taproot::LeafVersion::TapScript,
-                );
-
-                for key in ms.iter_pk_pkh() {
-                    let key = match key {
-                        miniscript::miniscript::iter::PkPkh::PlainPubkey(pk) => pk,
-                        miniscript::miniscript::iter::PkPkh::HashedPubkey(pk) => pk,
-                    };
-
-                    insert_path(&key, Some(leaf_hash));
-                }
-            }
-        }
-
-        answer
-    }
-}
-
 #[cfg(test)]
 mod test {
     use std::str::FromStr;
@@ -917,7 +851,7 @@ mod test {
 
     #[test]
     fn test_sh_wsh_sortedmulti_redeemscript() {
-        use super::{AsDerived, DescriptorScripts};
+        use miniscript::psbt::PsbtInputExt;
 
         let secp = Secp256k1::new();
 
@@ -925,11 +859,16 @@ mod test {
         let (descriptor, _) =
             into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet).unwrap();
 
-        let descriptor = descriptor.as_derived(0, &secp);
+        let descriptor = descriptor.at_derivation_index(0);
 
         let script = Script::from_str("5321022f533b667e2ea3b36e21961c9fe9dca340fbe0af5210173a83ae0337ab20a57621026bb53a98e810bd0ee61a0ed1164ba6c024786d76554e793e202dc6ce9c78c4ea2102d5b8a7d66a41ffdb6f4c53d61994022e886b4f45001fb158b95c9164d45f8ca3210324b75eead2c1f9c60e8adeb5e7009fec7a29afcdb30d829d82d09562fe8bae8521032d34f8932200833487bd294aa219dcbe000b9f9b3d824799541430009f0fa55121037468f8ea99b6c64788398b5ad25480cad08f4b0d65be54ce3a55fd206b5ae4722103f72d3d96663b0ea99b0aeb0d7f273cab11a8de37885f1dddc8d9112adb87169357ae").unwrap();
 
-        assert_eq!(descriptor.psbt_redeem_script(), Some(script.to_v0_p2wsh()));
-        assert_eq!(descriptor.psbt_witness_script(), Some(script));
+        let mut psbt_input = psbt::Input::default();
+        psbt_input
+            .update_with_descriptor_unchecked(&descriptor)
+            .unwrap();
+
+        assert_eq!(psbt_input.redeem_script, Some(script.to_v0_p2wsh()));
+        assert_eq!(psbt_input.witness_script, Some(script));
     }
 }
index 215078b60c3027e10cfab3f6ae9c95166e8b2de0..413fe6e3e4f70449cc705e3fbfd9280121a82983 100644 (file)
@@ -43,14 +43,17 @@ use std::fmt;
 use serde::ser::SerializeMap;
 use serde::{Serialize, Serializer};
 
-use bitcoin::hashes::*;
+use bitcoin::hashes::{hash160, ripemd160, sha256};
 use bitcoin::util::bip32::Fingerprint;
-use bitcoin::{PublicKey, XOnlyPublicKey};
+use bitcoin::{LockTime, PublicKey, Sequence, XOnlyPublicKey};
 
 use miniscript::descriptor::{
-    DescriptorPublicKey, DescriptorSinglePub, ShInner, SinglePubKey, SortedMultiVec, WshInner,
+    DescriptorPublicKey, ShInner, SinglePub, SinglePubKey, SortedMultiVec, WshInner,
+};
+use miniscript::hash256;
+use miniscript::{
+    Descriptor, Miniscript, Satisfier, ScriptContext, SigType, Terminal, ToPublicKey,
 };
-use miniscript::{Descriptor, Miniscript, MiniscriptKey, Satisfier, ScriptContext, Terminal};
 
 #[allow(unused_imports)]
 use log::{debug, error, info, trace};
@@ -58,7 +61,7 @@ use log::{debug, error, info, trace};
 use crate::descriptor::ExtractPolicy;
 use crate::keys::ExtScriptContext;
 use crate::wallet::signer::{SignerId, SignersContainer};
-use crate::wallet::utils::{self, After, Older, SecpCtx};
+use crate::wallet::utils::{After, Older, SecpCtx};
 
 use super::checksum::get_checksum;
 use super::error::Error;
@@ -81,11 +84,11 @@ pub enum PkOrF {
 impl PkOrF {
     fn from_key(k: &DescriptorPublicKey, secp: &SecpCtx) -> Self {
         match k {
-            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+            DescriptorPublicKey::Single(SinglePub {
                 key: SinglePubKey::FullKey(pk),
                 ..
             }) => PkOrF::Pubkey(*pk),
-            DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+            DescriptorPublicKey::Single(SinglePub {
                 key: SinglePubKey::XOnly(pk),
                 ..
             }) => PkOrF::XOnlyPubkey(*pk),
@@ -111,7 +114,7 @@ pub enum SatisfiableItem {
     /// Double SHA256 preimage hash
     Hash256Preimage {
         /// The digest value
-        hash: sha256d::Hash,
+        hash: hash256::Hash,
     },
     /// RIPEMD160 preimage hash
     Ripemd160Preimage {
@@ -125,13 +128,13 @@ pub enum SatisfiableItem {
     },
     /// Absolute timeclock timestamp
     AbsoluteTimelock {
-        /// The timestamp value
-        value: u32,
+        /// The timelock value
+        value: LockTime,
     },
     /// Relative timelock locktime
     RelativeTimelock {
-        /// The locktime value
-        value: u32,
+        /// The timelock value
+        value: Sequence,
     },
     /// Multi-signature public keys with threshold count
     Multisig {
@@ -438,32 +441,30 @@ pub struct Policy {
 }
 
 /// An extra condition that must be satisfied but that is out of control of the user
-#[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Serialize)]
+/// TODO: use `bitcoin::LockTime` and `bitcoin::Sequence`
+#[derive(Hash, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Default, Serialize)]
 pub struct Condition {
     /// Optional CheckSequenceVerify condition
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub csv: Option<u32>,
+    pub csv: Option<Sequence>,
     /// Optional timelock condition
     #[serde(skip_serializing_if = "Option::is_none")]
-    pub timelock: Option<u32>,
+    pub timelock: Option<LockTime>,
 }
 
 impl Condition {
-    fn merge_nlocktime(a: u32, b: u32) -> Result<u32, PolicyError> {
-        if (a < utils::BLOCKS_TIMELOCK_THRESHOLD) != (b < utils::BLOCKS_TIMELOCK_THRESHOLD) {
+    fn merge_nlocktime(a: LockTime, b: LockTime) -> Result<LockTime, PolicyError> {
+        if !a.is_same_unit(b) {
             Err(PolicyError::MixedTimelockUnits)
+        } else if a > b {
+            Ok(a)
         } else {
-            Ok(max(a, b))
+            Ok(b)
         }
     }
 
-    fn merge_nsequence(a: u32, b: u32) -> Result<u32, PolicyError> {
-        let mask = utils::SEQUENCE_LOCKTIME_TYPE_FLAG | utils::SEQUENCE_LOCKTIME_MASK;
-
-        let a = a & mask;
-        let b = b & mask;
-
-        if (a < utils::SEQUENCE_LOCKTIME_TYPE_FLAG) != (b < utils::SEQUENCE_LOCKTIME_TYPE_FLAG) {
+    fn merge_nsequence(a: Sequence, b: Sequence) -> Result<Sequence, PolicyError> {
+        if a.is_time_locked() != b.is_time_locked() {
             Err(PolicyError::MixedTimelockUnits)
         } else {
             Ok(max(a, b))
@@ -720,15 +721,18 @@ impl From<SatisfiableItem> for Policy {
 }
 
 fn signer_id(key: &DescriptorPublicKey, secp: &SecpCtx) -> SignerId {
+    // For consistency we always compute the key hash in "ecdsa" form (with the leading sign
+    // prefix) even if we are in a taproot descriptor. We just want some kind of unique identifier
+    // for a key, so it doesn't really matter how the identifier is computed.
     match key {
-        DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+        DescriptorPublicKey::Single(SinglePub {
             key: SinglePubKey::FullKey(pk),
             ..
-        }) => pk.to_pubkeyhash().into(),
-        DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+        }) => pk.to_pubkeyhash(SigType::Ecdsa).into(),
+        DescriptorPublicKey::Single(SinglePub {
             key: SinglePubKey::XOnly(pk),
             ..
-        }) => pk.to_pubkeyhash().into(),
+        }) => pk.to_pubkeyhash(SigType::Ecdsa).into(),
         DescriptorPublicKey::XPub(xpub) => xpub.root_fingerprint(secp).into(),
     }
 }
@@ -779,7 +783,7 @@ fn generic_sig_in_psbt<
 ) -> bool {
     //TODO check signature validity
     psbt.inputs.iter().all(|input| match key {
-        DescriptorPublicKey::SinglePub(DescriptorSinglePub { key, .. }) => check(input, key),
+        DescriptorPublicKey::Single(SinglePub { key, .. }) => check(input, key),
         DescriptorPublicKey::XPub(xpub) => {
             //TODO check actual derivation matches
             match extract(input, xpub.root_fingerprint(secp)) {
@@ -891,10 +895,13 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
                 Some(Ctx::make_signature(pubkey_hash, signers, build_sat, secp))
             }
             Terminal::After(value) => {
-                let mut policy: Policy = SatisfiableItem::AbsoluteTimelock { value: *value }.into();
+                let mut policy: Policy = SatisfiableItem::AbsoluteTimelock {
+                    value: value.into(),
+                }
+                .into();
                 policy.contribution = Satisfaction::Complete {
                     condition: Condition {
-                        timelock: Some(*value),
+                        timelock: Some(value.into()),
                         csv: None,
                     },
                 };
@@ -905,9 +912,11 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
                 } = build_sat
                 {
                     let after = After::new(Some(current_height), false);
-                    let after_sat = Satisfier::<bitcoin::PublicKey>::check_after(&after, *value);
-                    let inputs_sat = psbt_inputs_sat(psbt)
-                        .all(|sat| Satisfier::<bitcoin::PublicKey>::check_after(&sat, *value));
+                    let after_sat =
+                        Satisfier::<bitcoin::PublicKey>::check_after(&after, value.into());
+                    let inputs_sat = psbt_inputs_sat(psbt).all(|sat| {
+                        Satisfier::<bitcoin::PublicKey>::check_after(&sat, value.into())
+                    });
                     if after_sat && inputs_sat {
                         policy.satisfaction = policy.contribution.clone();
                     }
@@ -999,6 +1008,9 @@ impl<Ctx: ScriptContext + 'static> ExtractPolicy for Miniscript<DescriptorPublic
 
                 Policy::make_thresh(mapped, threshold)?
             }
+
+            // Unsupported
+            Terminal::RawPkH(_) => None,
         })
     }
 }
@@ -1124,14 +1136,12 @@ mod test {
     use crate::descriptor::{ExtractPolicy, IntoWalletDescriptor};
 
     use super::*;
-    use crate::descriptor::derived::AsDerived;
     use crate::descriptor::policy::SatisfiableItem::{EcdsaSignature, Multisig, Thresh};
     use crate::keys::{DescriptorKey, IntoDescriptorKey};
     use crate::wallet::signer::SignersContainer;
     use bitcoin::secp256k1::Secp256k1;
     use bitcoin::util::bip32;
     use bitcoin::Network;
-    use miniscript::DescriptorTrait;
     use std::str::FromStr;
     use std::sync::Arc;
 
@@ -1329,9 +1339,8 @@ mod test {
         let (wallet_desc, keymap) = desc
             .into_wallet_descriptor(&secp, Network::Testnet)
             .unwrap();
-        let single_key = wallet_desc.derive(0);
         let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = single_key
+        let policy = wallet_desc
             .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
             .unwrap()
             .unwrap();
@@ -1343,16 +1352,15 @@ mod test {
         let (wallet_desc, keymap) = desc
             .into_wallet_descriptor(&secp, Network::Testnet)
             .unwrap();
-        let single_key = wallet_desc.derive(0);
         let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = single_key
+        let policy = wallet_desc
             .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
             .unwrap()
             .unwrap();
 
-        assert!(matches!(&policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == &fingerprint));
+        assert!(matches!(policy.item, EcdsaSignature(PkOrF::Fingerprint(f)) if f == fingerprint));
         assert!(
-            matches!(&policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None)
+            matches!(policy.contribution, Satisfaction::Complete {condition} if condition.csv == None && condition.timelock == None)
         );
     }
 
@@ -1368,21 +1376,20 @@ mod test {
         let (wallet_desc, keymap) = desc
             .into_wallet_descriptor(&secp, Network::Testnet)
             .unwrap();
-        let single_key = wallet_desc.derive(0);
         let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
-        let policy = single_key
+        let policy = wallet_desc
             .extract_policy(&signers_container, BuildSatisfaction::None, &secp)
             .unwrap()
             .unwrap();
 
         assert!(
-            matches!(&policy.item, Multisig { keys, threshold } if threshold == &1
+            matches!(policy.item, Multisig { keys, threshold } if threshold == 1
             && keys[0] == PkOrF::Fingerprint(fingerprint0)
             && keys[1] == PkOrF::Fingerprint(fingerprint1))
         );
         assert!(
-            matches!(&policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == &2
-             && m == &1
+            matches!(policy.contribution, Satisfaction::PartialComplete { n, m, items, conditions, .. } if n == 2
+             && m == 1
              && items.len() == 2
              && conditions.contains_key(&vec![0])
              && conditions.contains_key(&vec![1])
@@ -1427,8 +1434,8 @@ mod test {
              && m == &2
              && items.len() == 3
              && conditions.get(&vec![0,1]).unwrap().iter().next().unwrap().csv.is_none()
-             && conditions.get(&vec![0,2]).unwrap().iter().next().unwrap().csv == Some(sequence)
-             && conditions.get(&vec![1,2]).unwrap().iter().next().unwrap().csv == Some(sequence)
+             && conditions.get(&vec![0,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence))
+             && conditions.get(&vec![1,2]).unwrap().iter().next().unwrap().csv == Some(Sequence(sequence))
             )
         );
     }
@@ -1574,7 +1581,7 @@ mod test {
             .unwrap();
 
         let addr = wallet_desc
-            .as_derived(0, &secp)
+            .at_derivation_index(0)
             .address(Network::Testnet)
             .unwrap();
         assert_eq!(
@@ -1646,7 +1653,7 @@ mod test {
         let signers_container = Arc::new(SignersContainer::build(keymap, &wallet_desc, &secp));
 
         let addr = wallet_desc
-            .as_derived(0, &secp)
+            .at_derivation_index(0)
             .address(Network::Testnet)
             .unwrap();
         assert_eq!(
index 91afa391063e8cb3661939c6e042630df9c314de..3adf6aaa58d3c0b5035aef1c976dbaee8be76552 100644 (file)
@@ -468,12 +468,10 @@ mod test {
     use std::str::FromStr;
 
     use super::*;
-    use crate::descriptor::derived::AsDerived;
     use crate::descriptor::{DescriptorError, DescriptorMeta};
     use crate::keys::ValidNetworks;
     use bitcoin::network::constants::Network::Regtest;
-    use bitcoin::secp256k1::Secp256k1;
-    use miniscript::descriptor::{DescriptorPublicKey, DescriptorTrait, KeyMap};
+    use miniscript::descriptor::{DescriptorPublicKey, KeyMap};
     use miniscript::Descriptor;
 
     // BIP44 `pkh(key/44'/{0,1}'/0'/{0,1}/*)`
@@ -517,17 +515,15 @@ mod test {
         is_fixed: bool,
         expected: &[&str],
     ) {
-        let secp = Secp256k1::new();
-
         let (desc, _key_map, _networks) = desc.unwrap();
         assert_eq!(desc.is_witness(), is_witness);
-        assert_eq!(!desc.is_deriveable(), is_fixed);
+        assert_eq!(!desc.has_wildcard(), is_fixed);
         for i in 0..expected.len() {
             let index = i as u32;
-            let child_desc = if !desc.is_deriveable() {
-                desc.as_derived_fixed(&secp)
+            let child_desc = if !desc.has_wildcard() {
+                desc.at_derivation_index(0)
             } else {
-                desc.as_derived(index, &secp)
+                desc.at_derivation_index(index)
             };
             let address = child_desc.address(Regtest).unwrap();
             assert_eq!(address.to_string(), *expected.get(i).unwrap());
index 66b5cf8b057fdd330257516d5b5403c3a2dd2804..4150eadc9e129455eee066d9d2bf8a4cc6304020 100644 (file)
@@ -103,6 +103,8 @@ pub enum Error {
     Encode(bitcoin::consensus::encode::Error),
     /// Miniscript error
     Miniscript(miniscript::Error),
+    /// Miniscript PSBT error
+    MiniscriptPsbt(MiniscriptPsbtError),
     /// BIP32 error
     Bip32(bitcoin::util::bip32::Error),
     /// An ECDSA error
@@ -147,6 +149,14 @@ pub enum Error {
     Rusqlite(rusqlite::Error),
 }
 
+/// Errors returned by miniscript when updating inconsistent PSBTs
+#[derive(Debug, Clone)]
+pub enum MiniscriptPsbtError {
+    Conversion(miniscript::descriptor::ConversionError),
+    UtxoUpdate(miniscript::psbt::UtxoUpdateError),
+    OutputUpdate(miniscript::psbt::OutputUpdateError),
+}
+
 /// Represents the last failed [`crate::blockchain::WalletSync`] sync attempt in which we were short
 /// on cached `scriptPubKey`s.
 #[derive(Debug)]
@@ -195,6 +205,7 @@ impl From<crate::keys::KeyError> for Error {
 
 impl_error!(bitcoin::consensus::encode::Error, Encode);
 impl_error!(miniscript::Error, Miniscript);
+impl_error!(MiniscriptPsbtError, MiniscriptPsbt);
 impl_error!(bitcoin::util::bip32::Error, Bip32);
 impl_error!(bitcoin::secp256k1::Error, Secp256k1);
 impl_error!(serde_json::Error, Json);
index 20ff581840bccfc290d6ed047810910fbbfc2e43..da541e1c5c83aceba8f317da8defef2482a5c705 100644 (file)
@@ -24,8 +24,8 @@ use bitcoin::{Network, PrivateKey, PublicKey, XOnlyPublicKey};
 
 use miniscript::descriptor::{Descriptor, DescriptorXKey, Wildcard};
 pub use miniscript::descriptor::{
-    DescriptorPublicKey, DescriptorSecretKey, DescriptorSinglePriv, DescriptorSinglePub, KeyMap,
-    SinglePubKey, SortedMultiVec,
+    DescriptorPublicKey, DescriptorSecretKey, KeyMap, SinglePriv, SinglePub, SinglePubKey,
+    SortedMultiVec,
 };
 pub use miniscript::ScriptContext;
 use miniscript::{Miniscript, Terminal};
@@ -110,7 +110,7 @@ impl<Ctx: ScriptContext> DescriptorKey<Ctx> {
                 let mut key_map = KeyMap::with_capacity(1);
 
                 let public = secret
-                    .as_public(secp)
+                    .to_public(secp)
                     .map_err(|e| miniscript::Error::Unexpected(e.to_string()))?;
                 key_map.insert(public.clone(), secret);
 
@@ -224,8 +224,8 @@ impl<Ctx: ScriptContext + 'static> ExtScriptContext for Ctx {
 /// use bdk::bitcoin::PublicKey;
 ///
 /// use bdk::keys::{
-///     mainnet_network, DescriptorKey, DescriptorPublicKey, DescriptorSinglePub,
-///     IntoDescriptorKey, KeyError, ScriptContext, SinglePubKey,
+///     mainnet_network, DescriptorKey, DescriptorPublicKey, IntoDescriptorKey, KeyError,
+///     ScriptContext, SinglePub, SinglePubKey,
 /// };
 ///
 /// pub struct MyKeyType {
@@ -235,7 +235,7 @@ impl<Ctx: ScriptContext + 'static> ExtScriptContext for Ctx {
 /// impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for MyKeyType {
 ///     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
 ///         Ok(DescriptorKey::from_public(
-///             DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+///             DescriptorPublicKey::Single(SinglePub {
 ///                 origin: None,
 ///                 key: SinglePubKey::FullKey(self.pubkey),
 ///             }),
@@ -842,7 +842,7 @@ impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for DescriptorKey<Ctx> {
 impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for DescriptorPublicKey {
     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
         let networks = match self {
-            DescriptorPublicKey::SinglePub(_) => any_network(),
+            DescriptorPublicKey::Single(_) => any_network(),
             DescriptorPublicKey::XPub(DescriptorXKey { xkey, .. })
                 if xkey.network == Network::Bitcoin =>
             {
@@ -857,7 +857,7 @@ impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for DescriptorPublicKey {
 
 impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for PublicKey {
     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+        DescriptorPublicKey::Single(SinglePub {
             key: SinglePubKey::FullKey(self),
             origin: None,
         })
@@ -867,7 +867,7 @@ impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for PublicKey {
 
 impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for XOnlyPublicKey {
     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        DescriptorPublicKey::SinglePub(DescriptorSinglePub {
+        DescriptorPublicKey::Single(SinglePub {
             key: SinglePubKey::XOnly(self),
             origin: None,
         })
@@ -878,7 +878,7 @@ impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for XOnlyPublicKey {
 impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for DescriptorSecretKey {
     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
         let networks = match &self {
-            DescriptorSecretKey::SinglePriv(sk) if sk.key.network == Network::Bitcoin => {
+            DescriptorSecretKey::Single(sk) if sk.key.network == Network::Bitcoin => {
                 mainnet_network()
             }
             DescriptorSecretKey::XPrv(DescriptorXKey { xkey, .. })
@@ -903,7 +903,7 @@ impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for &'_ str {
 
 impl<Ctx: ScriptContext> IntoDescriptorKey<Ctx> for PrivateKey {
     fn into_descriptor_key(self) -> Result<DescriptorKey<Ctx>, KeyError> {
-        DescriptorSecretKey::SinglePriv(DescriptorSinglePriv {
+        DescriptorSecretKey::Single(SinglePriv {
             key: self,
             origin: None,
         })
index a3d7c2b171b3dd0b29bf13d9508263f62e3b2c72..a048802e0c5236d286564ede6930c95edc026949 100644 (file)
@@ -1,8 +1,18 @@
+// Bitcoin Dev Kit
+//
+// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
+//
+// This file is licensed under the Apache License, Version 2.0 <LICENSE-APACHE
+// or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
+// You may not use this file except in accordance with one or both of these
+// licenses.
+
 use crate::testutils::TestIncomingTx;
 use bitcoin::consensus::encode::{deserialize, serialize};
 use bitcoin::hashes::hex::{FromHex, ToHex};
 use bitcoin::hashes::sha256d;
-use bitcoin::{Address, Amount, Script, Transaction, Txid, Witness};
+use bitcoin::{Address, Amount, PackedLockTime, Script, Sequence, Transaction, Txid, Witness};
 pub use bitcoincore_rpc::bitcoincore_rpc_json::AddressType;
 pub use bitcoincore_rpc::{Auth, Client as RpcClient, RpcApi};
 use core::str::FromStr;
@@ -110,7 +120,7 @@ impl TestClient {
         if let Some(true) = meta_tx.replaceable {
             // for some reason core doesn't set this field right
             for input in &mut tx.input {
-                input.sequence = 0xFFFFFFFD;
+                input.sequence = Sequence(0xFFFFFFFD);
             }
         }
 
@@ -164,6 +174,7 @@ impl TestClient {
         use bitcoin::blockdata::script::Builder;
         use bitcoin::blockdata::transaction::{OutPoint, TxIn, TxOut};
         use bitcoin::hash_types::{BlockHash, TxMerkleNode};
+        use bitcoin::hashes::Hash;
 
         let block_template: serde_json::Value = self
             .call("getblocktemplate", &[json!({"rules": ["segwit"]})])
@@ -176,7 +187,7 @@ impl TestClient {
                 block_template["previousblockhash"].as_str().unwrap(),
             )
             .unwrap(),
-            merkle_root: TxMerkleNode::default(),
+            merkle_root: TxMerkleNode::all_zeros(),
             time: block_template["curtime"].as_u64().unwrap() as u32,
             bits: u32::from_str_radix(block_template["bits"].as_str().unwrap(), 16).unwrap(),
             nonce: 0,
@@ -184,15 +195,15 @@ impl TestClient {
         debug!("header: {:#?}", header);
 
         let height = block_template["height"].as_u64().unwrap() as i64;
-        let witness_reserved_value: Vec<u8> = sha256d::Hash::default().as_ref().into();
+        let witness_reserved_value: Vec<u8> = sha256d::Hash::all_zeros().as_ref().into();
         // burn block subsidy and fees, not a big deal
         let mut coinbase_tx = Transaction {
             version: 1,
-            lock_time: 0,
+            lock_time: PackedLockTime(0),
             input: vec![TxIn {
                 previous_output: OutPoint::null(),
                 script_sig: Builder::new().push_int(height).into_script(),
-                sequence: 0xFFFFFFFF,
+                sequence: Sequence(0xFFFFFFFF),
                 witness: Witness::from_vec(vec![witness_reserved_value]),
             }],
             output: vec![],
@@ -1184,7 +1195,7 @@ macro_rules! bdk_blockchain_tests {
                 // 5. Verify 25_000 sats are received by test bitcoind node taproot wallet
 
                 let taproot_balance = taproot_wallet_client.get_balance(None, None).unwrap();
-                assert_eq!(taproot_balance.as_sat(), 25_000, "node has incorrect taproot wallet balance");
+                assert_eq!(taproot_balance.to_sat(), 25_000, "node has incorrect taproot wallet balance");
             }
 
             #[test]
index 82949ecc1ebc91c8bf7db83d5d2f5f469b970335..32f3951c2094209dd8047896d3f6d62223681a11 100644 (file)
@@ -101,25 +101,21 @@ impl TestIncomingTx {
 macro_rules! testutils {
     ( @external $descriptors:expr, $child:expr ) => ({
         use $crate::bitcoin::secp256k1::Secp256k1;
-        use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, DescriptorTrait};
-
-        use $crate::descriptor::AsDerived;
+        use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
 
         let secp = Secp256k1::new();
 
         let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &$descriptors.0).expect("Failed to parse descriptor in `testutils!(@external)`").0;
-        parsed.as_derived($child, &secp).address(bitcoin::Network::Regtest).expect("No address form")
+        parsed.at_derivation_index($child).address(bitcoin::Network::Regtest).expect("No address form")
     });
     ( @internal $descriptors:expr, $child:expr ) => ({
         use $crate::bitcoin::secp256k1::Secp256k1;
-        use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey, DescriptorTrait};
-
-        use $crate::descriptor::AsDerived;
+        use $crate::miniscript::descriptor::{Descriptor, DescriptorPublicKey};
 
         let secp = Secp256k1::new();
 
         let parsed = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, &$descriptors.1.expect("Missing internal descriptor")).expect("Failed to parse descriptor in `testutils!(@internal)`").0;
-        parsed.as_derived($child, &secp).address($crate::bitcoin::Network::Regtest).expect("No address form")
+        parsed.at_derivation_index($child).address($crate::bitcoin::Network::Regtest).expect("No address form")
     });
     ( @e $descriptors:expr, $child:expr ) => ({ testutils!(@external $descriptors, $child) });
     ( @i $descriptors:expr, $child:expr ) => ({ testutils!(@internal $descriptors, $child) });
@@ -186,49 +182,50 @@ macro_rules! testutils {
     ( @descriptors ( $external_descriptor:expr ) $( ( $internal_descriptor:expr ) )? $( ( @keys $( $keys:tt )* ) )* ) => ({
         use std::str::FromStr;
         use std::collections::HashMap;
+        use std::convert::Infallible;
+
         use $crate::miniscript::descriptor::Descriptor;
         use $crate::miniscript::TranslatePk;
 
+        struct Translator {
+            keys: HashMap<&'static str, (String, Option<String>, Option<String>)>,
+            is_internal: bool,
+        }
+
+        impl $crate::miniscript::Translator<String, String, Infallible> for Translator {
+            fn pk(&mut self, pk: &String) -> Result<String, Infallible> {
+                match self.keys.get(pk.as_str()) {
+                    Some((key, ext_path, int_path)) => {
+                        let path = if self.is_internal { int_path } else { ext_path };
+                        Ok(format!("{}{}", key, path.clone().unwrap_or_default()))
+                    }
+                    None => Ok(pk.clone()),
+                }
+            }
+            fn sha256(&mut self, sha256: &String) -> Result<String, Infallible> { Ok(sha256.clone()) }
+            fn hash256(&mut self, hash256: &String) -> Result<String, Infallible> { Ok(hash256.clone()) }
+            fn ripemd160(&mut self, ripemd160: &String) -> Result<String, Infallible> { Ok(ripemd160.clone()) }
+            fn hash160(&mut self, hash160: &String) -> Result<String, Infallible> { Ok(hash160.clone()) }
+        }
+
         #[allow(unused_assignments, unused_mut)]
-        let mut keys: HashMap<&'static str, (String, Option<String>, Option<String>)> = HashMap::new();
+        let mut keys = HashMap::new();
         $(
             keys = testutils!{ @keys $( $keys )* };
         )*
 
-        let external: Descriptor<String> = FromStr::from_str($external_descriptor).unwrap();
-        let external: Descriptor<String> = external.translate_pk_infallible::<_, _>(|k| {
-            if let Some((key, ext_path, _)) = keys.get(&k.as_str()) {
-                format!("{}{}", key, ext_path.as_ref().unwrap_or(&"".into()))
-            } else {
-                k.clone()
-            }
-        }, |kh| {
-            if let Some((key, ext_path, _)) = keys.get(&kh.as_str()) {
-                format!("{}{}", key, ext_path.as_ref().unwrap_or(&"".into()))
-            } else {
-                kh.clone()
-            }
+        let mut translator = Translator { keys, is_internal: false };
 
-        });
+        let external: Descriptor<String> = FromStr::from_str($external_descriptor).unwrap();
+        let external = external.translate_pk(&mut translator).expect("Infallible conversion");
         let external = external.to_string();
 
-        let internal = None::<String>$(.or({
-            let string_internal: Descriptor<String> = FromStr::from_str($internal_descriptor).unwrap();
+        translator.is_internal = true;
 
-            let string_internal: Descriptor<String> = string_internal.translate_pk_infallible::<_, _>(|k| {
-                if let Some((key, _, int_path)) = keys.get(&k.as_str()) {
-                    format!("{}{}", key, int_path.as_ref().unwrap_or(&"".into()))
-                } else {
-                    k.clone()
-                }
-            }, |kh| {
-                if let Some((key, _, int_path)) = keys.get(&kh.as_str()) {
-                    format!("{}{}", key, int_path.as_ref().unwrap_or(&"".into()))
-                } else {
-                    kh.clone()
-                }
-            });
-            Some(string_internal.to_string())
+        let internal = None::<String>$(.or({
+            let internal: Descriptor<String> = FromStr::from_str($internal_descriptor).unwrap();
+            let internal = internal.translate_pk(&mut translator).expect("Infallible conversion");
+            Some(internal.to_string())
         }))?;
 
         (external, internal)
index bae86477f03d038e3eb0a6c8a1d6db226eaf350e..59c264ee54c1db1e80cf1183b737706d742ecc76 100644 (file)
@@ -166,7 +166,7 @@ pub struct LocalUtxo {
 }
 
 /// A [`Utxo`] with its `satisfaction_weight`.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub struct WeightedUtxo {
     /// The weight of the witness data and `scriptSig` expressed in [weight units]. This is used to
     /// properly maintain the feerate when adding this input to a transaction during coin selection.
@@ -177,7 +177,7 @@ pub struct WeightedUtxo {
     pub utxo: Utxo,
 }
 
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 /// An unspent transaction output (UTXO).
 pub enum Utxo {
     /// A UTXO owned by the local wallet.
@@ -224,7 +224,7 @@ impl Utxo {
 }
 
 /// A wallet transaction
-#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Default)]
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
 pub struct TransactionDetails {
     /// Optional transaction
     pub transaction: Option<Transaction>,
index ebec69e4ec596bcea77899c28ac645f076f97c3d..30d07c1551dd234dd4a6f25b82bf9bc890d84608 100644 (file)
@@ -310,7 +310,7 @@ pub fn decide_change(remaining_amount: u64, fee_rate: FeeRate, drain_script: &Sc
     let drain_val = remaining_amount.saturating_sub(change_fee);
 
     if drain_val.is_dust(drain_script) {
-        let dust_threshold = drain_script.dust_value().as_sat();
+        let dust_threshold = drain_script.dust_value().to_sat();
         Excess::NoChange {
             dust_threshold,
             change_fee,
index 06b79d002b752f74b7fdb11e9d13e4ec5523ceb6..54d76f135e9625deb584b96d7ace77ff5447682f 100644 (file)
@@ -24,15 +24,13 @@ use std::sync::Arc;
 use bitcoin::secp256k1::Secp256k1;
 
 use bitcoin::consensus::encode::serialize;
-use bitcoin::util::{psbt, taproot};
+use bitcoin::util::psbt;
 use bitcoin::{
-    Address, EcdsaSighashType, Network, OutPoint, SchnorrSighashType, Script, Transaction, TxOut,
-    Txid, Witness,
+    Address, EcdsaSighashType, LockTime, Network, OutPoint, SchnorrSighashType, Script, Sequence,
+    Transaction, TxOut, Txid, Witness,
 };
 
-use miniscript::descriptor::DescriptorTrait;
-use miniscript::psbt::PsbtInputSatisfier;
-use miniscript::ToPublicKey;
+use miniscript::psbt::{PsbtExt, PsbtInputExt, PsbtInputSatisfier};
 
 #[allow(unused_imports)]
 use log::{debug, error, info, trace};
@@ -56,19 +54,17 @@ pub use utils::IsDust;
 use coin_selection::DefaultCoinSelectionAlgorithm;
 use signer::{SignOptions, SignerOrdering, SignersContainer, TransactionSigner};
 use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams};
-use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx};
+use utils::{check_nsequence_rbf, After, Older, SecpCtx};
 
 use crate::blockchain::{GetHeight, NoopProgress, Progress, WalletSync};
 use crate::database::memory::MemoryDatabase;
 use crate::database::{AnyDatabase, BatchDatabase, BatchOperations, DatabaseUtils, SyncTime};
-use crate::descriptor::derived::AsDerived;
 use crate::descriptor::policy::BuildSatisfaction;
 use crate::descriptor::{
-    get_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DerivedDescriptorMeta,
-    DescriptorMeta, DescriptorScripts, ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor,
-    Policy, XKeyUtils,
+    get_checksum, into_wallet_descriptor_checked, DerivedDescriptor, DescriptorMeta,
+    ExtendedDescriptor, ExtractPolicy, IntoWalletDescriptor, Policy, XKeyUtils,
 };
-use crate::error::Error;
+use crate::error::{Error, MiniscriptPsbtError};
 use crate::psbt::PsbtUtils;
 use crate::signer::SignerError;
 use crate::testutils;
@@ -137,7 +133,7 @@ pub enum AddressIndex {
 
 /// A derived address and the index it was found at
 /// For convenience this automatically derefs to `Address`
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Eq)]
 pub struct AddressInfo {
     /// Child index of this address
     pub index: u32,
@@ -247,7 +243,7 @@ where
 
         let address_result = self
             .get_descriptor_for_keychain(keychain)
-            .as_derived(incremented_index, &self.secp)
+            .at_derivation_index(incremented_index)
             .address(self.network);
 
         address_result
@@ -266,7 +262,7 @@ where
 
         let derived_key = self
             .get_descriptor_for_keychain(keychain)
-            .as_derived(current_index, &self.secp);
+            .at_derivation_index(current_index);
 
         let script_pubkey = derived_key.script_pubkey();
 
@@ -294,7 +290,7 @@ where
     // Return derived address for the descriptor of given [`KeychainKind`] at a specific index
     fn peek_address(&self, index: u32, keychain: KeychainKind) -> Result<AddressInfo, Error> {
         self.get_descriptor_for_keychain(keychain)
-            .as_derived(index, &self.secp)
+            .at_derivation_index(index)
             .address(self.network)
             .map(|address| AddressInfo {
                 index,
@@ -310,7 +306,7 @@ where
         self.set_index(keychain, index)?;
 
         self.get_descriptor_for_keychain(keychain)
-            .as_derived(index, &self.secp)
+            .at_derivation_index(index)
             .address(self.network)
             .map(|address| AddressInfo {
                 index,
@@ -359,7 +355,7 @@ where
     /// transaction output scripts.
     pub fn ensure_addresses_cached(&self, max_addresses: u32) -> Result<bool, Error> {
         let mut new_addresses_cached = false;
-        let max_address = match self.descriptor.is_deriveable() {
+        let max_address = match self.descriptor.has_wildcard() {
             false => 0,
             true => max_addresses,
         };
@@ -376,7 +372,7 @@ where
         }
 
         if let Some(change_descriptor) = &self.change_descriptor {
-            let max_address = match change_descriptor.is_deriveable() {
+            let max_address = match change_descriptor.has_wildcard() {
                 false => 0,
                 true => max_addresses,
             };
@@ -659,10 +655,9 @@ where
         // We use a match here instead of a map_or_else as it's way more readable :)
         let current_height = match params.current_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| sync_time.block_time.height),
+            None => self.database().get_sync_time()?.map(|sync_time| {
+                LockTime::from_height(sync_time.block_time.height).expect("Invalid height")
+            }),
             h => h,
         };
 
@@ -672,24 +667,33 @@ where
                 // Fee sniping can be partially prevented by setting the timelock
                 // to current_height. If we don't know the current_height,
                 // we default to 0.
-                let fee_sniping_height = current_height.unwrap_or(0);
+                let fee_sniping_height = current_height.unwrap_or(LockTime::ZERO);
+
                 // We choose the biggest between the required nlocktime and the fee sniping
                 // height
-                std::cmp::max(requirements.timelock.unwrap_or(0), fee_sniping_height)
+                match requirements.timelock {
+                    // No requirement, just use the fee_sniping_height
+                    None => 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,
+                    // There's a time-based requirement or a block-based requirement greater
+                    // than the fee_sniping_height use that value
+                    Some(value) => value,
+                }
             }
             // Specific nLockTime required and we have no constraints, so just set to that value
             Some(x) if requirements.timelock.is_none() => x,
             // Specific nLockTime required and it's compatible with the constraints
-            Some(x) if check_nlocktime(x, requirements.timelock.unwrap()) => x,
+            Some(x) if requirements.timelock.unwrap().is_same_unit(x) && x >= requirements.timelock.unwrap() => x,
             // Invalid nLockTime required
-            Some(x) => return Err(Error::Generic(format!("TxBuilder requested timelock of `{}`, but at least `{}` is required to spend from this script", x, requirements.timelock.unwrap())))
+            Some(x) => return Err(Error::Generic(format!("TxBuilder requested timelock of `{:?}`, but at least `{:?}` is required to spend from this script", x, requirements.timelock.unwrap())))
         };
 
         let n_sequence = match (params.rbf, requirements.csv) {
             // No RBF or CSV but there's an nLockTime, so the nSequence cannot be final
-            (None, None) if lock_time != 0 => 0xFFFFFFFE,
+            (None, None) if lock_time != LockTime::ZERO => Sequence::ENABLE_LOCKTIME_NO_RBF,
             // No RBF, CSV or nLockTime, make the transaction final
-            (None, None) => 0xFFFFFFFF,
+            (None, None) => Sequence::MAX,
 
             // No RBF requested, use the value from CSV. Note that this value is by definition
             // non-final, so even if a timelock is enabled this nSequence is fine, hence why we
@@ -697,7 +701,7 @@ where
             (None, Some(csv)) => csv,
 
             // RBF with a specific value but that value is too high
-            (Some(tx_builder::RbfValue::Value(rbf)), _) if rbf >= 0xFFFFFFFE => {
+            (Some(tx_builder::RbfValue::Value(rbf)), _) if !rbf.is_rbf() => {
                 return Err(Error::Generic(
                     "Cannot enable RBF with a nSequence >= 0xFFFFFFFE".into(),
                 ))
@@ -707,7 +711,7 @@ where
                 if !check_nsequence_rbf(rbf, csv) =>
             {
                 return Err(Error::Generic(format!(
-                    "Cannot enable RBF with nSequence `{}` given a required OP_CSV of `{}`",
+                    "Cannot enable RBF with nSequence `{:?}` given a required OP_CSV of `{:?}`",
                     rbf, csv
                 )))
             }
@@ -750,7 +754,7 @@ where
 
         let mut tx = Transaction {
             version,
-            lock_time,
+            lock_time: lock_time.into(),
             input: vec![],
             output: vec![],
         };
@@ -815,7 +819,7 @@ where
             params.drain_wallet,
             params.manually_selected_only,
             params.bumping_fee.is_some(), // we mandate confirmed transactions if we're bumping the fee
-            current_height,
+            current_height.map(LockTime::to_consensus_u32),
         )?;
 
         // get drain script
@@ -967,7 +971,11 @@ where
             Some(tx) => tx,
         };
         let mut tx = details.transaction.take().unwrap();
-        if !tx.input.iter().any(|txin| txin.sequence <= 0xFFFFFFFD) {
+        if !tx
+            .input
+            .iter()
+            .any(|txin| txin.sequence.to_consensus_u32() <= 0xFFFFFFFD)
+        {
             return Err(Error::IrreplaceableTransaction);
         }
 
@@ -1093,8 +1101,9 @@ where
         psbt: &mut psbt::PartiallySignedTransaction,
         sign_options: SignOptions,
     ) -> Result<bool, Error> {
-        // this helps us doing our job later
-        self.add_input_hd_keypaths(psbt)?;
+        // This adds all the PSBT metadata for the inputs, which will help us later figure out how
+        // to derive our keys
+        self.update_psbt_with_descriptor(psbt)?;
 
         // If we aren't allowed to use `witness_utxo`, ensure that every input (except p2tr and finalized ones)
         // has the `non_witness_utxo`
@@ -1295,21 +1304,18 @@ where
         }
     }
 
-    fn get_descriptor_for_txout(
-        &self,
-        txout: &TxOut,
-    ) -> Result<Option<DerivedDescriptor<'_>>, Error> {
+    fn get_descriptor_for_txout(&self, txout: &TxOut) -> Result<Option<DerivedDescriptor>, Error> {
         Ok(self
             .database
             .borrow()
             .get_path_from_script_pubkey(&txout.script_pubkey)?
             .map(|(keychain, child)| (self.get_descriptor_for_keychain(keychain), child))
-            .map(|(desc, child)| desc.as_derived(child, &self.secp)))
+            .map(|(desc, child)| desc.at_derivation_index(child)))
     }
 
     fn fetch_and_increment_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
         let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
-        let index = match descriptor.is_deriveable() {
+        let index = match descriptor.has_wildcard() {
             false => 0,
             true => self.database.borrow_mut().increment_last_index(keychain)?,
         };
@@ -1328,7 +1334,7 @@ where
 
     fn fetch_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
         let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
-        let index = match descriptor.is_deriveable() {
+        let index = match descriptor.has_wildcard() {
             false => Some(0),
             true => self.database.borrow_mut().get_last_index(keychain)?,
         };
@@ -1352,7 +1358,7 @@ where
         mut count: u32,
     ) -> Result<(), Error> {
         let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
-        if !descriptor.is_deriveable() {
+        if !descriptor.has_wildcard() {
             if from > 0 {
                 return Ok(());
             }
@@ -1365,7 +1371,7 @@ where
         let start_time = time::Instant::new();
         for i in from..(from + count) {
             address_batch.set_script_pubkey(
-                &descriptor.as_derived(i, &self.secp).script_pubkey(),
+                &descriptor.at_derivation_index(i).script_pubkey(),
                 keychain,
                 i,
             )?;
@@ -1569,52 +1575,7 @@ where
             }
         }
 
-        // probably redundant but it doesn't hurt...
-        self.add_input_hd_keypaths(&mut psbt)?;
-
-        // add metadata for the outputs
-        for (psbt_output, tx_output) in psbt.outputs.iter_mut().zip(psbt.unsigned_tx.output.iter())
-        {
-            if let Some((keychain, child)) = self
-                .database
-                .borrow()
-                .get_path_from_script_pubkey(&tx_output.script_pubkey)?
-            {
-                let (desc, _) = self._get_descriptor_for_keychain(keychain);
-                let derived_descriptor = desc.as_derived(child, &self.secp);
-
-                if let miniscript::Descriptor::Tr(tr) = &derived_descriptor {
-                    let tap_tree = if tr.taptree().is_some() {
-                        let mut builder = taproot::TaprootBuilder::new();
-                        for (depth, ms) in tr.iter_scripts() {
-                            let script = ms.encode();
-                            builder = builder.add_leaf(depth, script).expect(
-                                "Computing spend data on a valid Tree should always succeed",
-                            );
-                        }
-                        Some(
-                            psbt::TapTree::from_builder(builder)
-                                .expect("The tree should always be valid"),
-                        )
-                    } else {
-                        None
-                    };
-                    psbt_output.tap_tree = tap_tree;
-                    psbt_output
-                        .tap_key_origins
-                        .append(&mut derived_descriptor.get_tap_key_origins(&self.secp));
-                    psbt_output.tap_internal_key = Some(tr.internal_key().to_x_only_pubkey());
-                } else {
-                    psbt_output
-                        .bip32_derivation
-                        .append(&mut derived_descriptor.get_hd_keypaths(&self.secp));
-                }
-                if params.include_output_redeem_witness_script {
-                    psbt_output.witness_script = derived_descriptor.psbt_witness_script();
-                    psbt_output.redeem_script = derived_descriptor.psbt_redeem_script();
-                };
-            }
-        }
+        self.update_psbt_with_descriptor(&mut psbt)?;
 
         Ok(psbt)
     }
@@ -1640,29 +1601,11 @@ where
         };
 
         let desc = self.get_descriptor_for_keychain(keychain);
-        let derived_descriptor = desc.as_derived(child, &self.secp);
-
-        if let miniscript::Descriptor::Tr(tr) = &derived_descriptor {
-            psbt_input.tap_key_origins = derived_descriptor.get_tap_key_origins(&self.secp);
-            psbt_input.tap_internal_key = Some(tr.internal_key().to_x_only_pubkey());
-
-            let spend_info = tr.spend_info();
-            psbt_input.tap_merkle_root = spend_info.merkle_root();
-            psbt_input.tap_scripts = spend_info
-                .as_script_map()
-                .keys()
-                .filter_map(|script_ver| {
-                    spend_info
-                        .control_block(script_ver)
-                        .map(|cb| (cb, script_ver.clone()))
-                })
-                .collect();
-        } else {
-            psbt_input.bip32_derivation = derived_descriptor.get_hd_keypaths(&self.secp);
-        }
+        let derived_descriptor = desc.at_derivation_index(child);
 
-        psbt_input.redeem_script = derived_descriptor.psbt_redeem_script();
-        psbt_input.witness_script = derived_descriptor.psbt_witness_script();
+        psbt_input
+            .update_with_descriptor_unchecked(&derived_descriptor)
+            .map_err(MiniscriptPsbtError::Conversion)?;
 
         let prev_output = utxo.outpoint;
         if let Some(prev_tx) = self.database.borrow().get_raw_tx(&prev_output.txid)? {
@@ -1676,38 +1619,47 @@ where
         Ok(psbt_input)
     }
 
-    fn add_input_hd_keypaths(
+    fn update_psbt_with_descriptor(
         &self,
         psbt: &mut psbt::PartiallySignedTransaction,
     ) -> Result<(), Error> {
-        let mut input_utxos = Vec::with_capacity(psbt.inputs.len());
-        for n in 0..psbt.inputs.len() {
-            input_utxos.push(psbt.get_utxo_for(n).clone());
-        }
+        // We need to borrow `psbt` mutably within the loops, so we have to allocate a vec for all
+        // the input utxos and outputs
+        //
+        // Clippy complains that the collect is not required, but that's wrong
+        #[allow(clippy::needless_collect)]
+        let utxos = (0..psbt.inputs.len())
+            .filter_map(|i| psbt.get_utxo_for(i).map(|utxo| (true, i, utxo)))
+            .chain(
+                psbt.unsigned_tx
+                    .output
+                    .iter()
+                    .enumerate()
+                    .map(|(i, out)| (false, i, out.clone())),
+            )
+            .collect::<Vec<_>>();
 
-        // try to add hd_keypaths if we've already seen the output
-        for (psbt_input, out) in psbt.inputs.iter_mut().zip(input_utxos.iter()) {
-            if let Some(out) = out {
-                if let Some((keychain, child)) = self
-                    .database
-                    .borrow()
-                    .get_path_from_script_pubkey(&out.script_pubkey)?
-                {
-                    debug!("Found descriptor {:?}/{}", keychain, child);
-
-                    // merge hd_keypaths or tap_key_origins
-                    let desc = self.get_descriptor_for_keychain(keychain);
-                    if desc.is_taproot() {
-                        let mut tap_key_origins = desc
-                            .as_derived(child, &self.secp)
-                            .get_tap_key_origins(&self.secp);
-                        psbt_input.tap_key_origins.append(&mut tap_key_origins);
-                    } else {
-                        let mut hd_keypaths = desc
-                            .as_derived(child, &self.secp)
-                            .get_hd_keypaths(&self.secp);
-                        psbt_input.bip32_derivation.append(&mut hd_keypaths);
-                    }
+        // Try to figure out the keychain and derivation for every input and output
+        for (is_input, index, out) in utxos.into_iter() {
+            if let Some((keychain, child)) = self
+                .database
+                .borrow()
+                .get_path_from_script_pubkey(&out.script_pubkey)?
+            {
+                debug!(
+                    "Found descriptor for input #{} {:?}/{}",
+                    index, keychain, child
+                );
+
+                let desc = self.get_descriptor_for_keychain(keychain);
+                let desc = desc.at_derivation_index(child);
+
+                if is_input {
+                    psbt.update_input_with_descriptor(index, &desc)
+                        .map_err(MiniscriptPsbtError::UtxoUpdate)?;
+                } else {
+                    psbt.update_output_with_descriptor(index, &desc)
+                        .map_err(MiniscriptPsbtError::OutputUpdate)?;
                 }
             }
         }
@@ -1746,12 +1698,12 @@ where
 
         // We need to ensure descriptor is derivable to fullfil "missing cache", otherwise we will
         // end up with an infinite loop
-        let is_deriveable = self.descriptor.is_deriveable()
+        let has_wildcard = self.descriptor.has_wildcard()
             && (self.change_descriptor.is_none()
-                || self.change_descriptor.as_ref().unwrap().is_deriveable());
+                || self.change_descriptor.as_ref().unwrap().has_wildcard());
 
         // Restrict max rounds in case of faulty "missing cache" implementation by blockchain
-        let max_rounds = if is_deriveable { 100 } else { 1 };
+        let max_rounds = if has_wildcard { 100 } else { 1 };
 
         for _ in 0..max_rounds {
             let sync_res =
@@ -1886,7 +1838,7 @@ pub fn get_funded_wallet(
 
 #[cfg(test)]
 pub(crate) mod test {
-    use bitcoin::{util::psbt, Network};
+    use bitcoin::{util::psbt, Network, PackedLockTime, Sequence};
 
     use crate::database::Database;
     use crate::types::KeychainKind;
@@ -2199,7 +2151,7 @@ pub(crate) mod test {
 
         // 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.
-        assert_eq!(psbt.unsigned_tx.lock_time, 0);
+        assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(0));
     }
 
     #[test]
@@ -2224,7 +2176,7 @@ pub(crate) mod test {
         let (psbt, _) = builder.finish().unwrap();
 
         // current_height will override the last sync height
-        assert_eq!(psbt.unsigned_tx.lock_time, current_height);
+        assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(current_height));
     }
 
     #[test]
@@ -2247,7 +2199,10 @@ pub(crate) mod test {
         let (psbt, _) = builder.finish().unwrap();
 
         // If there's no current_height we're left with using the last sync height
-        assert_eq!(psbt.unsigned_tx.lock_time, sync_time.block_time.height);
+        assert_eq!(
+            psbt.unsigned_tx.lock_time,
+            PackedLockTime(sync_time.block_time.height)
+        );
     }
 
     #[test]
@@ -2258,7 +2213,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.unsigned_tx.lock_time, 100_000);
+        assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(100_000));
     }
 
     #[test]
@@ -2269,13 +2224,13 @@ pub(crate) mod test {
         builder
             .add_recipient(addr.script_pubkey(), 25_000)
             .current_height(630_001)
-            .nlocktime(630_000);
+            .nlocktime(LockTime::from_height(630_000).unwrap());
         let (psbt, _) = builder.finish().unwrap();
 
         // When we explicitly specify a nlocktime
         // we don't try any fee sniping prevention trick
         // (we ignore the current_height)
-        assert_eq!(psbt.unsigned_tx.lock_time, 630_000);
+        assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(630_000));
     }
 
     #[test]
@@ -2285,15 +2240,15 @@ pub(crate) mod test {
         let mut builder = wallet.build_tx();
         builder
             .add_recipient(addr.script_pubkey(), 25_000)
-            .nlocktime(630_000);
+            .nlocktime(LockTime::from_height(630_000).unwrap());
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.unsigned_tx.lock_time, 630_000);
+        assert_eq!(psbt.unsigned_tx.lock_time, PackedLockTime(630_000));
     }
 
     #[test]
     #[should_panic(
-        expected = "TxBuilder requested timelock of `50000`, but at least `100000` is required to spend from this script"
+        expected = "TxBuilder requested timelock of `Blocks(Height(50000))`, but at least `Blocks(Height(100000))` is required to spend from this script"
     )]
     fn test_create_tx_custom_locktime_incompatible_with_cltv() {
         let (wallet, _, _) = get_funded_wallet(get_test_single_sig_cltv());
@@ -2301,7 +2256,7 @@ pub(crate) mod test {
         let mut builder = wallet.build_tx();
         builder
             .add_recipient(addr.script_pubkey(), 25_000)
-            .nlocktime(50000);
+            .nlocktime(LockTime::from_height(50000).unwrap());
         builder.finish().unwrap();
     }
 
@@ -2313,7 +2268,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.unsigned_tx.input[0].sequence, 6);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(6));
     }
 
     #[test]
@@ -2327,12 +2282,12 @@ pub(crate) mod test {
         let (psbt, _) = builder.finish().unwrap();
         // When CSV is enabled it takes precedence over the rbf value (unless forced by the user).
         // It will be set to the OP_CSV value, in this case 6
-        assert_eq!(psbt.unsigned_tx.input[0].sequence, 6);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(6));
     }
 
     #[test]
     #[should_panic(
-        expected = "Cannot enable RBF with nSequence `3` given a required OP_CSV of `6`"
+        expected = "Cannot enable RBF with nSequence `Sequence(3)` given a required OP_CSV of `Sequence(6)`"
     )]
     fn test_create_tx_with_custom_rbf_csv() {
         let (wallet, _, _) = get_funded_wallet(get_test_single_sig_csv());
@@ -2340,7 +2295,7 @@ pub(crate) mod test {
         let mut builder = wallet.build_tx();
         builder
             .add_recipient(addr.script_pubkey(), 25_000)
-            .enable_rbf_with_sequence(3);
+            .enable_rbf_with_sequence(Sequence(3));
         builder.finish().unwrap();
     }
 
@@ -2352,7 +2307,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.unsigned_tx.input[0].sequence, 0xFFFFFFFE);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFE));
     }
 
     #[test]
@@ -2363,7 +2318,7 @@ pub(crate) mod test {
         let mut builder = wallet.build_tx();
         builder
             .add_recipient(addr.script_pubkey(), 25_000)
-            .enable_rbf_with_sequence(0xFFFFFFFE);
+            .enable_rbf_with_sequence(Sequence(0xFFFFFFFE));
         builder.finish().unwrap();
     }
 
@@ -2374,10 +2329,10 @@ pub(crate) mod test {
         let mut builder = wallet.build_tx();
         builder
             .add_recipient(addr.script_pubkey(), 25_000)
-            .enable_rbf_with_sequence(0xDEADBEEF);
+            .enable_rbf_with_sequence(Sequence(0xDEADBEEF));
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.unsigned_tx.input[0].sequence, 0xDEADBEEF);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xDEADBEEF));
     }
 
     #[test]
@@ -2404,7 +2359,7 @@ pub(crate) mod test {
         builder.add_recipient(addr.script_pubkey(), 25_000);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.unsigned_tx.input[0].sequence, 0xFFFFFFFF);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFF));
     }
 
     #[test]
@@ -2925,7 +2880,7 @@ pub(crate) mod test {
             .policy_path(path, KeychainKind::External);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.unsigned_tx.input[0].sequence, 0xFFFFFFFF);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(0xFFFFFFFF));
     }
 
     #[test]
@@ -2944,7 +2899,7 @@ pub(crate) mod test {
             .policy_path(path, KeychainKind::External);
         let (psbt, _) = builder.finish().unwrap();
 
-        assert_eq!(psbt.unsigned_tx.input[0].sequence, 144);
+        assert_eq!(psbt.unsigned_tx.input[0].sequence, Sequence(144));
     }
 
     #[test]
@@ -4797,7 +4752,7 @@ pub(crate) mod test {
         let (wallet, _, _) = get_funded_wallet(get_test_tr_repeated_key());
         let addr = wallet.get_address(AddressIndex::New).unwrap();
 
-        let path = vec![("rn4nre9c".to_string(), vec![0])]
+        let path = vec![("e5mmg3xh".to_string(), vec![0])]
             .into_iter()
             .collect();
 
@@ -4807,48 +4762,50 @@ pub(crate) mod test {
             .policy_path(path, KeychainKind::External);
         let (psbt, _) = builder.finish().unwrap();
 
+        let mut input_key_origins = psbt.inputs[0]
+            .tap_key_origins
+            .clone()
+            .into_iter()
+            .collect::<Vec<_>>();
+        input_key_origins.sort();
+
         assert_eq!(
-            psbt.inputs[0]
-                .tap_key_origins
-                .clone()
-                .into_iter()
-                .collect::<Vec<_>>(),
-            vec![(
-                from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"),
+            input_key_origins,
+            vec![
                 (
-                    vec![
-                        from_str!(
-                            "858ad7a7d7f270e2c490c4d6ba00c499e46b18fdd59ea3c2c47d20347110271e"
-                        ),
-                        from_str!(
-                            "f6e927ad4492c051fe325894a4f5f14538333b55a35f099876be42009ec8f903"
-                        )
-                    ],
-                    (Default::default(), Default::default())
+                    from_str!("b511bd5771e47ee27558b1765e87b541668304ec567721c7b880edc0a010da55"),
+                    (
+                        vec![],
+                        (FromStr::from_str("871fd295").unwrap(), vec![].into())
+                    )
+                ),
+                (
+                    from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"),
+                    (
+                        vec![
+                            from_str!(
+                                "858ad7a7d7f270e2c490c4d6ba00c499e46b18fdd59ea3c2c47d20347110271e"
+                            ),
+                            from_str!(
+                                "f6e927ad4492c051fe325894a4f5f14538333b55a35f099876be42009ec8f903"
+                            ),
+                        ],
+                        (FromStr::from_str("ece52657").unwrap(), vec![].into())
+                    )
                 )
-            )],
+            ],
             "Wrong input tap_key_origins"
         );
+
+        let mut output_key_origins = psbt.outputs[0]
+            .tap_key_origins
+            .clone()
+            .into_iter()
+            .collect::<Vec<_>>();
+        output_key_origins.sort();
+
         assert_eq!(
-            psbt.outputs[0]
-                .tap_key_origins
-                .clone()
-                .into_iter()
-                .collect::<Vec<_>>(),
-            vec![(
-                from_str!("2b0558078bec38694a84933d659303e2575dae7e91685911454115bfd64487e3"),
-                (
-                    vec![
-                        from_str!(
-                            "858ad7a7d7f270e2c490c4d6ba00c499e46b18fdd59ea3c2c47d20347110271e"
-                        ),
-                        from_str!(
-                            "f6e927ad4492c051fe325894a4f5f14538333b55a35f099876be42009ec8f903"
-                        )
-                    ],
-                    (Default::default(), Default::default())
-                )
-            )],
+            input_key_origins, output_key_origins,
             "Wrong output tap_key_origins"
         );
     }
@@ -5100,7 +5057,7 @@ pub(crate) mod test {
     #[test]
     fn test_taproot_script_spend_sign_include_some_leaves() {
         use crate::signer::TapLeavesOptions;
-        use crate::wallet::taproot::TapLeafHash;
+        use bitcoin::util::taproot::TapLeafHash;
 
         let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
         let addr = wallet.get_address(AddressIndex::New).unwrap();
@@ -5142,7 +5099,7 @@ pub(crate) mod test {
     #[test]
     fn test_taproot_script_spend_sign_exclude_some_leaves() {
         use crate::signer::TapLeavesOptions;
-        use crate::wallet::taproot::TapLeafHash;
+        use bitcoin::util::taproot::TapLeafHash;
 
         let (wallet, _, _) = get_funded_wallet(get_test_tr_with_taptree_both_priv());
         let addr = wallet.get_address(AddressIndex::New).unwrap();
index 01bf5628ae777f2e418e67b52ff29852e18d3950..84d38826a940d092c3dc2807c66e451f67966365 100644 (file)
@@ -96,10 +96,10 @@ use bitcoin::{secp256k1, XOnlyPublicKey};
 use bitcoin::{EcdsaSighashType, PrivateKey, PublicKey, SchnorrSighashType, Script};
 
 use miniscript::descriptor::{
-    Descriptor, DescriptorPublicKey, DescriptorSecretKey, DescriptorSinglePriv, DescriptorXKey,
-    KeyMap, SinglePubKey,
+    Descriptor, DescriptorPublicKey, DescriptorSecretKey, DescriptorXKey, KeyMap, SinglePriv,
+    SinglePubKey,
 };
-use miniscript::{Legacy, MiniscriptKey, Segwitv0, Tap};
+use miniscript::{Legacy, Segwitv0, SigType, Tap, ToPublicKey};
 
 use super::utils::SecpCtx;
 use crate::descriptor::{DescriptorMeta, XKeyUtils};
@@ -369,11 +369,11 @@ impl InputSigner for SignerWrapper<DescriptorXKey<ExtendedPrivKey>> {
 
 impl SignerCommon for SignerWrapper<PrivateKey> {
     fn id(&self, secp: &SecpCtx) -> SignerId {
-        SignerId::from(self.public_key(secp).to_pubkeyhash())
+        SignerId::from(self.public_key(secp).to_pubkeyhash(SigType::Ecdsa))
     }
 
     fn descriptor_secret_key(&self) -> Option<DescriptorSecretKey> {
-        Some(DescriptorSecretKey::SinglePriv(DescriptorSinglePriv {
+        Some(DescriptorSecretKey::Single(SinglePriv {
             key: self.signer,
             origin: None,
         }))
@@ -517,13 +517,13 @@ fn sign_psbt_schnorr(
     let keypair = match leaf_hash {
         None => keypair
             .tap_tweak(secp, psbt_input.tap_merkle_root)
-            .into_inner(),
+            .to_inner(),
         Some(_) => keypair, // no tweak for script spend
     };
 
     let msg = &Message::from_slice(&hash.into_inner()[..]).unwrap();
     let sig = secp.sign_schnorr(msg, &keypair);
-    secp.verify_schnorr(&sig, msg, &XOnlyPublicKey::from_keypair(&keypair))
+    secp.verify_schnorr(&sig, msg, &XOnlyPublicKey::from_keypair(&keypair).0)
         .expect("invalid or corrupted schnorr signature");
 
     let final_signature = schnorr::SchnorrSig { sig, hash_ty };
@@ -576,7 +576,7 @@ impl SignersContainer {
         self.0
             .values()
             .filter_map(|signer| signer.descriptor_secret_key())
-            .filter_map(|secret| secret.as_public(secp).ok().map(|public| (public, secret)))
+            .filter_map(|secret| secret.to_public(secp).ok().map(|public| (public, secret)))
             .collect()
     }
 
@@ -601,8 +601,13 @@ impl SignersContainer {
             };
 
             match secret {
-                DescriptorSecretKey::SinglePriv(private_key) => container.add_external(
-                    SignerId::from(private_key.key.public_key(secp).to_pubkeyhash()),
+                DescriptorSecretKey::Single(private_key) => container.add_external(
+                    SignerId::from(
+                        private_key
+                            .key
+                            .public_key(secp)
+                            .to_pubkeyhash(SigType::Ecdsa),
+                    ),
                     SignerOrdering::default(),
                     Arc::new(SignerWrapper::new(private_key.key, ctx)),
                 ),
@@ -732,7 +737,7 @@ pub struct SignOptions {
 }
 
 /// Customize which taproot script-path leaves the signer should sign.
-#[derive(Debug, Clone, PartialEq)]
+#[derive(Debug, Clone, PartialEq, Eq)]
 pub enum TapLeavesOptions {
     /// The signer will sign all the leaves it has a key for.
     All,
index 6464967ad9fa4f2d862fb580d9114d790d3917de..6d52b8d9040c1d3bca901bcdbf630c49b2d8500d 100644 (file)
@@ -42,9 +42,7 @@ use std::default::Default;
 use std::marker::PhantomData;
 
 use bitcoin::util::psbt::{self, PartiallySignedTransaction as Psbt};
-use bitcoin::{OutPoint, Script, Transaction};
-
-use miniscript::descriptor::DescriptorTrait;
+use bitcoin::{LockTime, OutPoint, Script, Sequence, Transaction};
 
 use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
 use crate::{database::BatchDatabase, Error, Utxo, Wallet};
@@ -139,7 +137,7 @@ pub(crate) struct TxParams {
     pub(crate) manually_selected_only: bool,
     pub(crate) sighash: Option<psbt::PsbtSighashType>,
     pub(crate) ordering: TxOrdering,
-    pub(crate) locktime: Option<u32>,
+    pub(crate) locktime: Option<LockTime>,
     pub(crate) rbf: Option<RbfValue>,
     pub(crate) version: Option<Version>,
     pub(crate) change_policy: ChangeSpendPolicy,
@@ -147,7 +145,7 @@ pub(crate) struct TxParams {
     pub(crate) add_global_xpubs: bool,
     pub(crate) include_output_redeem_witness_script: bool,
     pub(crate) bumping_fee: Option<PreviousFee>,
-    pub(crate) current_height: Option<u32>,
+    pub(crate) current_height: Option<LockTime>,
     pub(crate) allow_dust: bool,
 }
 
@@ -426,7 +424,7 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
     /// Use a specific nLockTime while creating the transaction
     ///
     /// This can cause conflicts if the wallet's descriptors contain an "after" (OP_CLTV) operator.
-    pub fn nlocktime(&mut self, locktime: u32) -> &mut Self {
+    pub fn nlocktime(&mut self, locktime: LockTime) -> &mut Self {
         self.params.locktime = Some(locktime);
         self
     }
@@ -541,7 +539,7 @@ impl<'a, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderContext>
     ///
     /// If the `nsequence` is higher than `0xFFFFFFFD` an error will be thrown, since it would not
     /// be a valid nSequence to signal RBF.
-    pub fn enable_rbf_with_sequence(&mut self, nsequence: u32) -> &mut Self {
+    pub fn enable_rbf_with_sequence(&mut self, nsequence: Sequence) -> &mut Self {
         self.params.rbf = Some(RbfValue::Value(nsequence));
         self
     }
@@ -558,7 +556,7 @@ 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.
     pub fn current_height(&mut self, height: u32) -> &mut Self {
-        self.params.current_height = Some(height);
+        self.params.current_height = Some(LockTime::from_height(height).expect("Invalid height"));
         self
     }
 
@@ -736,13 +734,13 @@ impl Default for Version {
 #[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
 pub(crate) enum RbfValue {
     Default,
-    Value(u32),
+    Value(Sequence),
 }
 
 impl RbfValue {
-    pub(crate) fn get_value(&self) -> u32 {
+    pub(crate) fn get_value(&self) -> Sequence {
         match self {
-            RbfValue::Default => 0xFFFFFFFD,
+            RbfValue::Default => Sequence::ENABLE_RBF_NO_LOCKTIME,
             RbfValue::Value(v) => *v,
         }
     }
@@ -858,10 +856,12 @@ mod test {
     }
 
     fn get_test_utxos() -> Vec<LocalUtxo> {
+        use bitcoin::hashes::Hash;
+
         vec![
             LocalUtxo {
                 outpoint: OutPoint {
-                    txid: Default::default(),
+                    txid: bitcoin::Txid::from_inner([0; 32]),
                     vout: 0,
                 },
                 txout: Default::default(),
@@ -870,7 +870,7 @@ mod test {
             },
             LocalUtxo {
                 outpoint: OutPoint {
-                    txid: Default::default(),
+                    txid: bitcoin::Txid::from_inner([0; 32]),
                     vout: 1,
                 },
                 txout: Default::default(),
index 8c072da025ce5cc7228e2310fb75e9d57e80a163..163d417eef27fd5afe282376fd8c36e636150c0e 100644 (file)
@@ -9,23 +9,11 @@
 // You may not use this file except in accordance with one or both of these
 // licenses.
 
-use bitcoin::blockdata::script::Script;
 use bitcoin::secp256k1::{All, Secp256k1};
+use bitcoin::{LockTime, Script, Sequence};
 
 use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
 
-// MSB of the nSequence. If set there's no consensus-constraint, so it must be disabled when
-// spending using CSV in order to enforce CSV rules
-pub(crate) const SEQUENCE_LOCKTIME_DISABLE_FLAG: u32 = 1 << 31;
-// When nSequence is lower than this flag the timelock is interpreted as block-height-based,
-// otherwise it's time-based
-pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
-// Mask for the bits used to express the timelock
-pub(crate) const SEQUENCE_LOCKTIME_MASK: u32 = 0x0000FFFF;
-
-// Threshold for nLockTime to be considered a block-height-based timelock rather than time-based
-pub(crate) const BLOCKS_TIMELOCK_THRESHOLD: u32 = 500000000;
-
 /// Trait to check if a value is below the dust limit.
 /// We are performing dust value calculation for a given script public key using rust-bitcoin to
 /// keep it compatible with network dust rate
@@ -38,7 +26,7 @@ pub trait IsDust {
 
 impl IsDust for u64 {
     fn is_dust(&self, script: &Script) -> bool {
-        *self < script.dust_value().as_sat()
+        *self < script.dust_value().to_sat()
     }
 }
 
@@ -56,19 +44,15 @@ impl After {
     }
 }
 
-pub(crate) fn check_nsequence_rbf(rbf: u32, csv: u32) -> bool {
-    // This flag cannot be set in the nSequence when spending using OP_CSV
-    if rbf & SEQUENCE_LOCKTIME_DISABLE_FLAG != 0 {
+pub(crate) fn check_nsequence_rbf(rbf: Sequence, csv: Sequence) -> bool {
+    // The RBF value must enable relative timelocks
+    if !rbf.is_relative_lock_time() {
         return false;
     }
 
-    let mask = SEQUENCE_LOCKTIME_TYPE_FLAG | SEQUENCE_LOCKTIME_MASK;
-    let rbf = rbf & mask;
-    let csv = csv & mask;
-
     // Both values should be represented in the same unit (either time-based or
     // block-height based)
-    if (rbf < SEQUENCE_LOCKTIME_TYPE_FLAG) != (csv < SEQUENCE_LOCKTIME_TYPE_FLAG) {
+    if rbf.is_time_locked() != csv.is_time_locked() {
         return false;
     }
 
@@ -80,24 +64,10 @@ pub(crate) fn check_nsequence_rbf(rbf: u32, csv: u32) -> bool {
     true
 }
 
-pub(crate) fn check_nlocktime(nlocktime: u32, required: u32) -> bool {
-    // Both values should be expressed in the same unit
-    if (nlocktime < BLOCKS_TIMELOCK_THRESHOLD) != (required < BLOCKS_TIMELOCK_THRESHOLD) {
-        return false;
-    }
-
-    // The value should be at least `required`
-    if nlocktime < required {
-        return false;
-    }
-
-    true
-}
-
 impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
-    fn check_after(&self, n: u32) -> bool {
+    fn check_after(&self, n: LockTime) -> bool {
         if let Some(current_height) = self.current_height {
-            current_height >= n
+            current_height >= n.to_consensus_u32()
         } else {
             self.assume_height_reached
         }
@@ -125,10 +95,15 @@ impl Older {
 }
 
 impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
-    fn check_older(&self, n: u32) -> bool {
+    fn check_older(&self, n: Sequence) -> bool {
         if let Some(current_height) = self.current_height {
             // TODO: test >= / >
-            current_height as u64 >= self.create_height.unwrap_or(0) as u64 + n as u64
+            current_height
+                >= self
+                    .create_height
+                    .unwrap_or(0)
+                    .checked_add(n.to_consensus_u32())
+                    .expect("Overflowing addition")
         } else {
             self.assume_height_reached
         }
@@ -139,11 +114,12 @@ pub(crate) type SecpCtx = Secp256k1<All>;
 
 #[cfg(test)]
 mod test {
-    use super::{
-        check_nlocktime, check_nsequence_rbf, IsDust, BLOCKS_TIMELOCK_THRESHOLD,
-        SEQUENCE_LOCKTIME_TYPE_FLAG,
-    };
-    use crate::bitcoin::Address;
+    // When nSequence is lower than this flag the timelock is interpreted as block-height-based,
+    // otherwise it's time-based
+    pub(crate) const SEQUENCE_LOCKTIME_TYPE_FLAG: u32 = 1 << 22;
+
+    use super::{check_nsequence_rbf, IsDust};
+    use crate::bitcoin::{Address, Sequence};
     use std::str::FromStr;
 
     #[test]
@@ -165,66 +141,40 @@ mod test {
 
     #[test]
     fn test_check_nsequence_rbf_msb_set() {
-        let result = check_nsequence_rbf(0x80000000, 5000);
+        let result = check_nsequence_rbf(Sequence(0x80000000), Sequence(5000));
         assert!(!result);
     }
 
     #[test]
     fn test_check_nsequence_rbf_lt_csv() {
-        let result = check_nsequence_rbf(4000, 5000);
+        let result = check_nsequence_rbf(Sequence(4000), Sequence(5000));
         assert!(!result);
     }
 
     #[test]
     fn test_check_nsequence_rbf_different_unit() {
-        let result = check_nsequence_rbf(SEQUENCE_LOCKTIME_TYPE_FLAG + 5000, 5000);
+        let result =
+            check_nsequence_rbf(Sequence(SEQUENCE_LOCKTIME_TYPE_FLAG + 5000), Sequence(5000));
         assert!(!result);
     }
 
     #[test]
     fn test_check_nsequence_rbf_mask() {
-        let result = check_nsequence_rbf(0x3f + 10_000, 5000);
+        let result = check_nsequence_rbf(Sequence(0x3f + 10_000), Sequence(5000));
         assert!(result);
     }
 
     #[test]
     fn test_check_nsequence_rbf_same_unit_blocks() {
-        let result = check_nsequence_rbf(10_000, 5000);
+        let result = check_nsequence_rbf(Sequence(10_000), Sequence(5000));
         assert!(result);
     }
 
     #[test]
     fn test_check_nsequence_rbf_same_unit_time() {
         let result = check_nsequence_rbf(
-            SEQUENCE_LOCKTIME_TYPE_FLAG + 10_000,
-            SEQUENCE_LOCKTIME_TYPE_FLAG + 5000,
-        );
-        assert!(result);
-    }
-
-    #[test]
-    fn test_check_nlocktime_lt_cltv() {
-        let result = check_nlocktime(4000, 5000);
-        assert!(!result);
-    }
-
-    #[test]
-    fn test_check_nlocktime_different_unit() {
-        let result = check_nlocktime(BLOCKS_TIMELOCK_THRESHOLD + 5000, 5000);
-        assert!(!result);
-    }
-
-    #[test]
-    fn test_check_nlocktime_same_unit_blocks() {
-        let result = check_nlocktime(10_000, 5000);
-        assert!(result);
-    }
-
-    #[test]
-    fn test_check_nlocktime_same_unit_time() {
-        let result = check_nlocktime(
-            BLOCKS_TIMELOCK_THRESHOLD + 10_000,
-            BLOCKS_TIMELOCK_THRESHOLD + 5000,
+            Sequence(SEQUENCE_LOCKTIME_TYPE_FLAG + 10_000),
+            Sequence(SEQUENCE_LOCKTIME_TYPE_FLAG + 5000),
         );
         assert!(result);
     }