2021-03-03 13:22:05 -08:00
|
|
|
// Bitcoin Dev Kit
|
|
|
|
// Written in 2020 by Alekos Filini <alekos.filini@gmail.com>
|
2020-08-31 11:26:36 +02:00
|
|
|
//
|
2021-03-03 13:22:05 -08:00
|
|
|
// Copyright (c) 2020-2021 Bitcoin Dev Kit Developers
|
2020-08-31 11:26:36 +02:00
|
|
|
//
|
2021-03-03 13:22:05 -08:00
|
|
|
// 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.
|
2020-08-31 11:26:36 +02:00
|
|
|
|
2020-09-04 11:44:49 +02:00
|
|
|
//! Descriptors
|
|
|
|
//!
|
|
|
|
//! This module contains generic utilities to work with descriptors, plus some re-exported types
|
|
|
|
//! from [`miniscript`].
|
|
|
|
|
2023-01-10 15:10:02 +11:00
|
|
|
use crate::collections::BTreeMap;
|
|
|
|
use alloc::string::String;
|
|
|
|
use alloc::vec::Vec;
|
2020-01-27 22:02:55 +01:00
|
|
|
|
2023-10-16 19:51:53 +11:00
|
|
|
use bitcoin::bip32::{ChildNumber, DerivationPath, Fingerprint, KeySource, Xpub};
|
2023-07-19 15:27:48 +02:00
|
|
|
use bitcoin::{key::XOnlyPublicKey, secp256k1, PublicKey};
|
|
|
|
use bitcoin::{psbt, taproot};
|
2022-10-25 11:15:43 +02:00
|
|
|
use bitcoin::{Network, TxOut};
|
2020-01-27 22:02:55 +01:00
|
|
|
|
2022-11-23 11:22:03 +01:00
|
|
|
use miniscript::descriptor::{
|
2023-07-19 15:27:48 +02:00
|
|
|
DefiniteDescriptorKey, DescriptorMultiXKey, DescriptorSecretKey, DescriptorType,
|
|
|
|
DescriptorXKey, InnerXKey, KeyMap, SinglePubKey, Wildcard,
|
2022-11-23 11:22:03 +01:00
|
|
|
};
|
2022-03-09 18:39:58 +01:00
|
|
|
pub use miniscript::{
|
2023-07-19 15:27:48 +02:00
|
|
|
Descriptor, DescriptorPublicKey, Legacy, Miniscript, ScriptContext, Segwitv0,
|
2021-09-14 12:56:00 +00:00
|
|
|
};
|
2022-10-25 11:15:43 +02:00
|
|
|
use miniscript::{ForEachKey, MiniscriptKey, TranslatePk};
|
2020-01-27 22:02:55 +01:00
|
|
|
|
2021-04-02 16:39:18 +02:00
|
|
|
use crate::descriptor::policy::BuildSatisfaction;
|
|
|
|
|
2020-02-15 21:27:51 +01:00
|
|
|
pub mod checksum;
|
2020-12-16 16:10:22 +01:00
|
|
|
#[doc(hidden)]
|
|
|
|
pub mod dsl;
|
2020-01-27 22:02:55 +01:00
|
|
|
pub mod error;
|
2020-02-07 23:22:28 +01:00
|
|
|
pub mod policy;
|
2020-09-22 16:12:09 +02:00
|
|
|
pub mod template;
|
2020-01-27 22:02:55 +01:00
|
|
|
|
2022-10-24 12:05:49 -05:00
|
|
|
pub use self::checksum::calc_checksum;
|
|
|
|
use self::checksum::calc_checksum_bytes;
|
2021-01-11 13:12:01 +01:00
|
|
|
pub use self::error::Error as DescriptorError;
|
2020-02-15 21:27:51 +01:00
|
|
|
pub use self::policy::Policy;
|
2020-12-10 11:38:42 +01:00
|
|
|
use self::template::DescriptorTemplateOut;
|
2021-02-12 23:02:13 -08:00
|
|
|
use crate::keys::{IntoDescriptorKey, KeyError};
|
2020-08-15 23:21:13 +02:00
|
|
|
use crate::wallet::signer::SignersContainer;
|
2021-02-02 20:06:40 -05:00
|
|
|
use crate::wallet::utils::SecpCtx;
|
2020-02-07 23:22:28 +01:00
|
|
|
|
2020-09-04 11:44:49 +02:00
|
|
|
/// Alias for a [`Descriptor`] that can contain extended keys using [`DescriptorPublicKey`]
|
2020-08-12 12:51:50 +02:00
|
|
|
pub type ExtendedDescriptor = Descriptor<DescriptorPublicKey>;
|
2020-09-04 11:44:49 +02:00
|
|
|
|
2021-02-02 20:06:40 -05:00
|
|
|
/// Alias for a [`Descriptor`] that contains extended **derived** keys
|
2022-10-25 11:15:43 +02:00
|
|
|
pub type DerivedDescriptor = Descriptor<DefiniteDescriptorKey>;
|
2021-02-02 20:06:40 -05:00
|
|
|
|
2020-09-04 11:44:49 +02:00
|
|
|
/// Alias for the type of maps that represent derivation paths in a [`psbt::Input`] or
|
|
|
|
/// [`psbt::Output`]
|
|
|
|
///
|
2023-07-19 15:27:48 +02:00
|
|
|
/// [`psbt::Input`]: bitcoin::psbt::Input
|
|
|
|
/// [`psbt::Output`]: bitcoin::psbt::Output
|
2022-04-14 17:20:46 +02:00
|
|
|
pub type HdKeyPaths = BTreeMap<secp256k1::PublicKey, KeySource>;
|
2020-05-10 17:42:02 +02:00
|
|
|
|
2022-04-27 16:29:02 +02:00
|
|
|
/// Alias for the type of maps that represent taproot key origins in a [`psbt::Input`] or
|
|
|
|
/// [`psbt::Output`]
|
|
|
|
///
|
2023-07-19 15:27:48 +02:00
|
|
|
/// [`psbt::Input`]: bitcoin::psbt::Input
|
|
|
|
/// [`psbt::Output`]: bitcoin::psbt::Output
|
|
|
|
pub type TapKeyOrigins = BTreeMap<XOnlyPublicKey, (Vec<taproot::TapLeafHash>, KeySource)>;
|
2022-04-27 16:29:02 +02:00
|
|
|
|
2020-09-21 15:44:07 +02:00
|
|
|
/// Trait for types which can be converted into an [`ExtendedDescriptor`] and a [`KeyMap`] usable by a wallet in a specific [`Network`]
|
2021-02-12 22:34:43 -08:00
|
|
|
pub trait IntoWalletDescriptor {
|
2020-12-13 20:36:38 -08:00
|
|
|
/// Convert to wallet descriptor
|
2021-02-11 11:00:48 -08:00
|
|
|
fn into_wallet_descriptor(
|
2020-09-21 15:44:07 +02:00
|
|
|
self,
|
2021-02-02 20:06:40 -05:00
|
|
|
secp: &SecpCtx,
|
2020-09-21 15:44:07 +02:00
|
|
|
network: Network,
|
2021-01-11 13:12:01 +01:00
|
|
|
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError>;
|
2020-09-18 16:31:03 +02:00
|
|
|
}
|
|
|
|
|
2021-02-12 22:34:43 -08:00
|
|
|
impl IntoWalletDescriptor for &str {
|
2021-02-11 11:00:48 -08:00
|
|
|
fn into_wallet_descriptor(
|
2020-09-21 15:44:07 +02:00
|
|
|
self,
|
2021-02-02 20:06:40 -05:00
|
|
|
secp: &SecpCtx,
|
2020-09-21 15:44:07 +02:00
|
|
|
network: Network,
|
2021-01-11 13:12:01 +01:00
|
|
|
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
|
2022-09-29 13:06:03 +08:00
|
|
|
let descriptor = match self.split_once('#') {
|
|
|
|
Some((desc, original_checksum)) => {
|
2022-10-24 12:05:49 -05:00
|
|
|
let checksum = calc_checksum_bytes(desc)?;
|
2022-09-29 14:24:28 +08:00
|
|
|
if original_checksum.as_bytes() != checksum {
|
2022-09-29 13:06:03 +08:00
|
|
|
return Err(DescriptorError::InvalidDescriptorChecksum);
|
|
|
|
}
|
|
|
|
desc
|
2020-09-21 15:44:07 +02:00
|
|
|
}
|
2022-09-29 13:06:03 +08:00
|
|
|
None => self,
|
2020-09-21 15:44:07 +02:00
|
|
|
};
|
|
|
|
|
2021-02-11 11:00:48 -08:00
|
|
|
ExtendedDescriptor::parse_descriptor(secp, descriptor)?
|
|
|
|
.into_wallet_descriptor(secp, network)
|
2020-09-18 16:31:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 22:34:43 -08:00
|
|
|
impl IntoWalletDescriptor for &String {
|
2021-02-11 11:00:48 -08:00
|
|
|
fn into_wallet_descriptor(
|
2020-09-21 15:44:07 +02:00
|
|
|
self,
|
2021-02-02 20:06:40 -05:00
|
|
|
secp: &SecpCtx,
|
2020-09-21 15:44:07 +02:00
|
|
|
network: Network,
|
2021-01-11 13:12:01 +01:00
|
|
|
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
|
2021-02-11 11:00:48 -08:00
|
|
|
self.as_str().into_wallet_descriptor(secp, network)
|
2020-09-21 15:44:07 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 22:34:43 -08:00
|
|
|
impl IntoWalletDescriptor for ExtendedDescriptor {
|
2021-02-11 11:00:48 -08:00
|
|
|
fn into_wallet_descriptor(
|
2020-09-21 15:44:07 +02:00
|
|
|
self,
|
2021-02-02 20:06:40 -05:00
|
|
|
secp: &SecpCtx,
|
2020-09-21 15:44:07 +02:00
|
|
|
network: Network,
|
2021-01-11 13:12:01 +01:00
|
|
|
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
|
2021-02-11 11:00:48 -08:00
|
|
|
(self, KeyMap::default()).into_wallet_descriptor(secp, network)
|
2020-09-18 16:31:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 22:34:43 -08:00
|
|
|
impl IntoWalletDescriptor for (ExtendedDescriptor, KeyMap) {
|
2021-02-11 11:00:48 -08:00
|
|
|
fn into_wallet_descriptor(
|
2020-09-21 15:44:07 +02:00
|
|
|
self,
|
2021-02-02 20:06:40 -05:00
|
|
|
secp: &SecpCtx,
|
2020-09-21 15:44:07 +02:00
|
|
|
network: Network,
|
2021-01-11 13:12:01 +01:00
|
|
|
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
|
2020-09-21 15:44:07 +02:00
|
|
|
use crate::keys::DescriptorKey;
|
|
|
|
|
2022-10-25 11:15:43 +02:00
|
|
|
struct Translator<'s, 'd> {
|
|
|
|
secp: &'s SecpCtx,
|
|
|
|
descriptor: &'d ExtendedDescriptor,
|
|
|
|
network: Network,
|
|
|
|
}
|
|
|
|
|
2023-07-19 15:27:48 +02:00
|
|
|
impl<'s, 'd> miniscript::Translator<DescriptorPublicKey, String, DescriptorError>
|
2022-10-25 11:15:43 +02:00
|
|
|
for Translator<'s, 'd>
|
|
|
|
{
|
2023-07-19 15:27:48 +02:00
|
|
|
fn pk(&mut self, pk: &DescriptorPublicKey) -> Result<String, DescriptorError> {
|
2022-10-25 11:15:43 +02:00
|
|
|
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) {
|
2023-07-19 15:27:48 +02:00
|
|
|
Ok(Default::default())
|
2022-10-25 11:15:43 +02:00
|
|
|
} else {
|
|
|
|
Err(DescriptorError::Key(KeyError::InvalidNetwork))
|
|
|
|
}
|
2020-10-09 12:03:47 +02:00
|
|
|
}
|
2022-10-25 11:15:43 +02:00
|
|
|
fn sha256(
|
|
|
|
&mut self,
|
|
|
|
_sha256: &<DescriptorPublicKey as MiniscriptKey>::Sha256,
|
2023-07-19 15:27:48 +02:00
|
|
|
) -> Result<String, DescriptorError> {
|
2022-10-25 11:15:43 +02:00
|
|
|
Ok(Default::default())
|
|
|
|
}
|
|
|
|
fn hash256(
|
|
|
|
&mut self,
|
|
|
|
_hash256: &<DescriptorPublicKey as MiniscriptKey>::Hash256,
|
2023-07-19 15:27:48 +02:00
|
|
|
) -> Result<String, DescriptorError> {
|
2022-10-25 11:15:43 +02:00
|
|
|
Ok(Default::default())
|
|
|
|
}
|
|
|
|
fn ripemd160(
|
|
|
|
&mut self,
|
|
|
|
_ripemd160: &<DescriptorPublicKey as MiniscriptKey>::Ripemd160,
|
2023-07-19 15:27:48 +02:00
|
|
|
) -> Result<String, DescriptorError> {
|
2022-10-25 11:15:43 +02:00
|
|
|
Ok(Default::default())
|
|
|
|
}
|
|
|
|
fn hash160(
|
|
|
|
&mut self,
|
|
|
|
_hash160: &<DescriptorPublicKey as MiniscriptKey>::Hash160,
|
2023-07-19 15:27:48 +02:00
|
|
|
) -> Result<String, DescriptorError> {
|
2022-10-25 11:15:43 +02:00
|
|
|
Ok(Default::default())
|
|
|
|
}
|
|
|
|
}
|
2020-09-21 15:44:07 +02:00
|
|
|
|
2020-10-09 12:03:47 +02:00
|
|
|
// check the network for the keys
|
2023-07-19 15:27:48 +02:00
|
|
|
use miniscript::TranslateErr;
|
|
|
|
match self.0.translate_pk(&mut Translator {
|
2022-10-25 11:15:43 +02:00
|
|
|
secp,
|
|
|
|
network,
|
|
|
|
descriptor: &self.0,
|
2023-07-19 15:27:48 +02:00
|
|
|
}) {
|
|
|
|
Ok(_) => {}
|
|
|
|
Err(TranslateErr::TranslatorErr(e)) => return Err(e),
|
|
|
|
Err(TranslateErr::OuterError(e)) => return Err(e.into()),
|
|
|
|
}
|
2020-09-21 15:44:07 +02:00
|
|
|
|
2022-10-25 11:15:43 +02:00
|
|
|
Ok(self)
|
2020-09-18 16:31:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-12 22:34:43 -08:00
|
|
|
impl IntoWalletDescriptor for DescriptorTemplateOut {
|
2021-02-11 11:00:48 -08:00
|
|
|
fn into_wallet_descriptor(
|
2020-09-21 15:44:07 +02:00
|
|
|
self,
|
2021-02-02 20:06:40 -05:00
|
|
|
_secp: &SecpCtx,
|
2020-09-21 15:44:07 +02:00
|
|
|
network: Network,
|
2021-01-11 13:12:01 +01:00
|
|
|
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
|
2022-10-25 11:15:43 +02:00
|
|
|
struct Translator {
|
|
|
|
network: Network,
|
|
|
|
}
|
2020-09-21 15:44:07 +02:00
|
|
|
|
2022-10-25 11:15:43 +02:00
|
|
|
impl miniscript::Translator<DescriptorPublicKey, DescriptorPublicKey, DescriptorError>
|
|
|
|
for Translator
|
|
|
|
{
|
|
|
|
fn pk(
|
|
|
|
&mut self,
|
|
|
|
pk: &DescriptorPublicKey,
|
|
|
|
) -> Result<DescriptorPublicKey, DescriptorError> {
|
2020-10-09 12:03:47 +02:00
|
|
|
// 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
|
|
|
|
// them are valid. here we reset the network to make sure the wallet struct gets a
|
|
|
|
// descriptor with the right network everywhere.
|
|
|
|
let pk = match pk {
|
|
|
|
DescriptorPublicKey::XPub(ref xpub) => {
|
|
|
|
let mut xpub = xpub.clone();
|
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(chain): use `minimal_non_dust()` instead of `dust_value()`
fix(chain): use `compute_txid()` instead of `txid`
deps(testenv): bump `electrsd` to `0.28.0`
deps(electrum): bump `electrum-client` to `0.20.0`
fix(electrum): use `compute_txid()` instead of `txid`
deps(esplora): bump `esplora-client` to `0.8.0`
deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to
`0.19.0`
fix(bitcoind_rpc): use `compute_txid()` instead of `txid`
fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected
`Signature` fields
fix(sqlite): use `compute_txid()` instead of `txid`
deps(hwi): bump `hwi` to `0.9.0`
deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(wallet): use `compute_txid()` and `minimal_non_dust()`
- update to use `compute_txid()` instead of deprecated `txid()`
- update to use `minimal_non_dust()` instead of `dust_value()`
- remove unused `bitcoin::hex::FromHex`.
fix(wallet): uses `.into` conversion on `Network` for `NetworkKind`
- uses `.into()` when appropriate, otherwise use the explicit
`NetworkKind`, and it's `.is_mainnet()` method.
fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError`
fix(wallet): fields on taproot, and ecdsa `Signature` structure
fix(wallet/wallet): convert `Weight` to `usize` for now
- converts the `bitcoin-units::Weight` type to `usize` with help of
`to_wu()` method.
- it should be updated/refactored in the future to handle the `Weight`
type throughout the code instead of current `usize`, only converting
it for now.
- allows the usage of deprecated `is_provably_unspendable()`, needs
further discussion if suggested `is_op_return` is suitable.
- update the expect field to `signature`, as it was renamed from `sig`.
fix(wallet/wallet): use `is_op_return` instead of
`is_provably_unspendable`
fix(wallet/wallet): use `relative::Locktime` instead of `Sequence`
fix(wallet/descriptor): use `ParsePublicKeyError`
fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and
`RelLockTime` to `absolute::LockTime` and `relative::LockTime`
fix(wallet/wallet): use `Message::from_digest()` instead of relying on
deprecated `ThirtyTwoByteHash` trait.
fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it
internally
fix(wallet/wallet): remove `0x` prefix from expected `TxId` display
fix(examples): use `compute_txid()` instead of `txid`
fix(ci): remove usage of `bitcoin/no-std` feature
- remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
|
|
|
xpub.xkey.network = self.network.into();
|
2020-10-09 12:03:47 +02:00
|
|
|
|
|
|
|
DescriptorPublicKey::XPub(xpub)
|
|
|
|
}
|
|
|
|
other => other.clone(),
|
|
|
|
};
|
2020-09-21 15:44:07 +02:00
|
|
|
|
2020-10-09 12:03:47 +02:00
|
|
|
Ok(pk)
|
|
|
|
}
|
2022-10-25 11:15:43 +02:00
|
|
|
miniscript::translate_hash_clone!(
|
|
|
|
DescriptorPublicKey,
|
|
|
|
DescriptorPublicKey,
|
|
|
|
DescriptorError
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-11-23 11:22:03 +01:00
|
|
|
let (desc, keymap, networks) = self;
|
|
|
|
|
|
|
|
if !networks.contains(&network) {
|
2022-10-25 11:15:43 +02:00
|
|
|
return Err(DescriptorError::Key(KeyError::InvalidNetwork));
|
|
|
|
}
|
2020-10-09 12:03:47 +02:00
|
|
|
|
2022-11-23 11:22:03 +01:00
|
|
|
// fixup the network for keys that need it in the descriptor
|
2023-07-19 15:27:48 +02:00
|
|
|
use miniscript::TranslateErr;
|
|
|
|
let translated = match desc.translate_pk(&mut Translator { network }) {
|
|
|
|
Ok(descriptor) => descriptor,
|
|
|
|
Err(TranslateErr::TranslatorErr(e)) => return Err(e),
|
|
|
|
Err(TranslateErr::OuterError(e)) => return Err(e.into()),
|
|
|
|
};
|
2022-11-23 11:22:03 +01:00
|
|
|
// ...and in the key map
|
|
|
|
let fixed_keymap = keymap
|
|
|
|
.into_iter()
|
|
|
|
.map(|(mut k, mut v)| {
|
|
|
|
match (&mut k, &mut v) {
|
|
|
|
(DescriptorPublicKey::XPub(xpub), DescriptorSecretKey::XPrv(xprv)) => {
|
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(chain): use `minimal_non_dust()` instead of `dust_value()`
fix(chain): use `compute_txid()` instead of `txid`
deps(testenv): bump `electrsd` to `0.28.0`
deps(electrum): bump `electrum-client` to `0.20.0`
fix(electrum): use `compute_txid()` instead of `txid`
deps(esplora): bump `esplora-client` to `0.8.0`
deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to
`0.19.0`
fix(bitcoind_rpc): use `compute_txid()` instead of `txid`
fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected
`Signature` fields
fix(sqlite): use `compute_txid()` instead of `txid`
deps(hwi): bump `hwi` to `0.9.0`
deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(wallet): use `compute_txid()` and `minimal_non_dust()`
- update to use `compute_txid()` instead of deprecated `txid()`
- update to use `minimal_non_dust()` instead of `dust_value()`
- remove unused `bitcoin::hex::FromHex`.
fix(wallet): uses `.into` conversion on `Network` for `NetworkKind`
- uses `.into()` when appropriate, otherwise use the explicit
`NetworkKind`, and it's `.is_mainnet()` method.
fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError`
fix(wallet): fields on taproot, and ecdsa `Signature` structure
fix(wallet/wallet): convert `Weight` to `usize` for now
- converts the `bitcoin-units::Weight` type to `usize` with help of
`to_wu()` method.
- it should be updated/refactored in the future to handle the `Weight`
type throughout the code instead of current `usize`, only converting
it for now.
- allows the usage of deprecated `is_provably_unspendable()`, needs
further discussion if suggested `is_op_return` is suitable.
- update the expect field to `signature`, as it was renamed from `sig`.
fix(wallet/wallet): use `is_op_return` instead of
`is_provably_unspendable`
fix(wallet/wallet): use `relative::Locktime` instead of `Sequence`
fix(wallet/descriptor): use `ParsePublicKeyError`
fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and
`RelLockTime` to `absolute::LockTime` and `relative::LockTime`
fix(wallet/wallet): use `Message::from_digest()` instead of relying on
deprecated `ThirtyTwoByteHash` trait.
fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it
internally
fix(wallet/wallet): remove `0x` prefix from expected `TxId` display
fix(examples): use `compute_txid()` instead of `txid`
fix(ci): remove usage of `bitcoin/no-std` feature
- remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
|
|
|
xpub.xkey.network = network.into();
|
|
|
|
xprv.xkey.network = network.into();
|
2022-11-23 11:22:03 +01:00
|
|
|
}
|
|
|
|
(_, DescriptorSecretKey::Single(key)) => {
|
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(chain): use `minimal_non_dust()` instead of `dust_value()`
fix(chain): use `compute_txid()` instead of `txid`
deps(testenv): bump `electrsd` to `0.28.0`
deps(electrum): bump `electrum-client` to `0.20.0`
fix(electrum): use `compute_txid()` instead of `txid`
deps(esplora): bump `esplora-client` to `0.8.0`
deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to
`0.19.0`
fix(bitcoind_rpc): use `compute_txid()` instead of `txid`
fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected
`Signature` fields
fix(sqlite): use `compute_txid()` instead of `txid`
deps(hwi): bump `hwi` to `0.9.0`
deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(wallet): use `compute_txid()` and `minimal_non_dust()`
- update to use `compute_txid()` instead of deprecated `txid()`
- update to use `minimal_non_dust()` instead of `dust_value()`
- remove unused `bitcoin::hex::FromHex`.
fix(wallet): uses `.into` conversion on `Network` for `NetworkKind`
- uses `.into()` when appropriate, otherwise use the explicit
`NetworkKind`, and it's `.is_mainnet()` method.
fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError`
fix(wallet): fields on taproot, and ecdsa `Signature` structure
fix(wallet/wallet): convert `Weight` to `usize` for now
- converts the `bitcoin-units::Weight` type to `usize` with help of
`to_wu()` method.
- it should be updated/refactored in the future to handle the `Weight`
type throughout the code instead of current `usize`, only converting
it for now.
- allows the usage of deprecated `is_provably_unspendable()`, needs
further discussion if suggested `is_op_return` is suitable.
- update the expect field to `signature`, as it was renamed from `sig`.
fix(wallet/wallet): use `is_op_return` instead of
`is_provably_unspendable`
fix(wallet/wallet): use `relative::Locktime` instead of `Sequence`
fix(wallet/descriptor): use `ParsePublicKeyError`
fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and
`RelLockTime` to `absolute::LockTime` and `relative::LockTime`
fix(wallet/wallet): use `Message::from_digest()` instead of relying on
deprecated `ThirtyTwoByteHash` trait.
fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it
internally
fix(wallet/wallet): remove `0x` prefix from expected `TxId` display
fix(examples): use `compute_txid()` instead of `txid`
fix(ci): remove usage of `bitcoin/no-std` feature
- remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
|
|
|
key.key.network = network.into();
|
2022-11-23 11:22:03 +01:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
|
|
|
|
(k, v)
|
|
|
|
})
|
|
|
|
.collect();
|
2020-09-21 15:44:07 +02:00
|
|
|
|
2022-11-23 11:22:03 +01:00
|
|
|
Ok((translated, fixed_keymap))
|
2020-09-18 16:31:03 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-15 11:33:47 -05:00
|
|
|
/// Wrapper for `IntoWalletDescriptor` that performs additional checks on the keys contained in the
|
|
|
|
/// descriptor
|
|
|
|
pub(crate) fn into_wallet_descriptor_checked<T: IntoWalletDescriptor>(
|
|
|
|
inner: T,
|
|
|
|
secp: &SecpCtx,
|
|
|
|
network: Network,
|
|
|
|
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
|
|
|
|
let (descriptor, keymap) = inner.into_wallet_descriptor(secp, network)?;
|
|
|
|
|
|
|
|
// Ensure the keys don't contain any hardened derivation steps or hardened wildcards
|
|
|
|
let descriptor_contains_hardened_steps = descriptor.for_any_key(|k| {
|
|
|
|
if let DescriptorPublicKey::XPub(DescriptorXKey {
|
|
|
|
derivation_path,
|
|
|
|
wildcard,
|
|
|
|
..
|
2022-10-25 11:15:43 +02:00
|
|
|
}) = k
|
2021-02-15 11:33:47 -05:00
|
|
|
{
|
|
|
|
return *wildcard == Wildcard::Hardened
|
|
|
|
|| derivation_path.into_iter().any(ChildNumber::is_hardened);
|
|
|
|
}
|
|
|
|
|
|
|
|
false
|
|
|
|
});
|
|
|
|
if descriptor_contains_hardened_steps {
|
|
|
|
return Err(DescriptorError::HardenedDerivationXpub);
|
|
|
|
}
|
|
|
|
|
2023-07-19 18:48:20 +02:00
|
|
|
if descriptor.is_multipath() {
|
|
|
|
return Err(DescriptorError::MultiPath);
|
|
|
|
}
|
|
|
|
|
2022-09-24 13:48:05 +02:00
|
|
|
// Run miniscript's sanity check, which will look for duplicated keys and other potential
|
|
|
|
// issues
|
|
|
|
descriptor.sanity_check()?;
|
2021-02-23 15:57:43 +01:00
|
|
|
|
2021-02-15 11:33:47 -05:00
|
|
|
Ok((descriptor, keymap))
|
|
|
|
}
|
|
|
|
|
2021-01-11 13:12:01 +01:00
|
|
|
#[doc(hidden)]
|
|
|
|
/// Used internally mainly by the `descriptor!()` and `fragment!()` macros
|
|
|
|
pub trait CheckMiniscript<Ctx: miniscript::ScriptContext> {
|
2021-11-23 12:59:08 -05:00
|
|
|
fn check_miniscript(&self) -> Result<(), miniscript::Error>;
|
2021-01-11 13:12:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<Ctx: miniscript::ScriptContext, Pk: miniscript::MiniscriptKey> CheckMiniscript<Ctx>
|
|
|
|
for miniscript::Miniscript<Pk, Ctx>
|
|
|
|
{
|
2021-11-23 12:59:08 -05:00
|
|
|
fn check_miniscript(&self) -> Result<(), miniscript::Error> {
|
2021-01-11 13:12:01 +01:00
|
|
|
Ctx::check_global_validity(self)?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-04 11:44:49 +02:00
|
|
|
/// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
|
2020-08-12 12:51:50 +02:00
|
|
|
pub trait ExtractPolicy {
|
2020-12-13 20:36:38 -08:00
|
|
|
/// Extract the spending [`policy`]
|
2020-11-16 22:07:38 +01:00
|
|
|
fn extract_policy(
|
|
|
|
&self,
|
2020-11-17 12:05:32 -06:00
|
|
|
signers: &SignersContainer,
|
2021-04-02 16:39:18 +02:00
|
|
|
psbt: BuildSatisfaction,
|
2020-11-16 22:07:38 +01:00
|
|
|
secp: &SecpCtx,
|
2021-01-11 13:12:01 +01:00
|
|
|
) -> Result<Option<Policy>, DescriptorError>;
|
2020-02-07 23:22:28 +01:00
|
|
|
}
|
2020-01-27 22:02:55 +01:00
|
|
|
|
2020-09-04 11:44:49 +02:00
|
|
|
pub(crate) trait XKeyUtils {
|
2020-11-16 22:07:38 +01:00
|
|
|
fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint;
|
2020-02-15 21:27:51 +01:00
|
|
|
}
|
|
|
|
|
2023-07-19 15:27:48 +02:00
|
|
|
impl<T> XKeyUtils for DescriptorMultiXKey<T>
|
|
|
|
where
|
|
|
|
T: InnerXKey,
|
|
|
|
{
|
|
|
|
fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
|
|
|
|
match self.origin {
|
|
|
|
Some((fingerprint, _)) => fingerprint,
|
|
|
|
None => self.xkey.xkey_fingerprint(secp),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-14 12:56:00 +00:00
|
|
|
impl<T> XKeyUtils for DescriptorXKey<T>
|
|
|
|
where
|
|
|
|
T: InnerXKey,
|
|
|
|
{
|
2020-11-16 22:07:38 +01:00
|
|
|
fn root_fingerprint(&self, secp: &SecpCtx) -> Fingerprint {
|
2020-10-09 12:03:47 +02:00
|
|
|
match self.origin {
|
2020-10-07 14:18:50 -07:00
|
|
|
Some((fingerprint, _)) => fingerprint,
|
2021-09-14 12:56:00 +00:00
|
|
|
None => self.xkey.xkey_fingerprint(secp),
|
2020-08-12 12:51:50 +02:00
|
|
|
}
|
2020-01-27 22:02:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-02 20:06:40 -05:00
|
|
|
pub(crate) trait DescriptorMeta {
|
2020-08-12 12:51:50 +02:00
|
|
|
fn is_witness(&self) -> bool;
|
2022-04-14 17:20:46 +02:00
|
|
|
fn is_taproot(&self) -> bool;
|
2023-10-16 19:51:53 +11:00
|
|
|
fn get_extended_keys(&self) -> Vec<DescriptorXKey<Xpub>>;
|
2023-03-02 19:08:33 +01:00
|
|
|
fn derive_from_hd_keypaths(
|
2021-02-02 20:06:40 -05:00
|
|
|
&self,
|
2021-03-30 16:33:07 +02:00
|
|
|
hd_keypaths: &HdKeyPaths,
|
2023-03-02 19:08:33 +01:00
|
|
|
secp: &SecpCtx,
|
2022-10-25 11:15:43 +02:00
|
|
|
) -> Option<DerivedDescriptor>;
|
2023-03-02 19:08:33 +01:00
|
|
|
fn derive_from_tap_key_origins(
|
2022-04-29 12:59:09 +02:00
|
|
|
&self,
|
|
|
|
tap_key_origins: &TapKeyOrigins,
|
2023-03-02 19:08:33 +01:00
|
|
|
secp: &SecpCtx,
|
2022-10-25 11:15:43 +02:00
|
|
|
) -> Option<DerivedDescriptor>;
|
2023-03-02 19:08:33 +01:00
|
|
|
fn derive_from_psbt_key_origins(
|
2022-04-29 12:59:09 +02:00
|
|
|
&self,
|
|
|
|
key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
|
2023-03-02 19:08:33 +01:00
|
|
|
secp: &SecpCtx,
|
2022-10-25 11:15:43 +02:00
|
|
|
) -> Option<DerivedDescriptor>;
|
2023-03-02 19:08:33 +01:00
|
|
|
fn derive_from_psbt_input(
|
2020-11-16 22:07:38 +01:00
|
|
|
&self,
|
|
|
|
psbt_input: &psbt::Input,
|
|
|
|
utxo: Option<TxOut>,
|
2023-03-02 19:08:33 +01:00
|
|
|
secp: &SecpCtx,
|
2022-10-25 11:15:43 +02:00
|
|
|
) -> Option<DerivedDescriptor>;
|
2020-01-27 22:02:55 +01:00
|
|
|
}
|
|
|
|
|
2021-02-02 20:06:40 -05:00
|
|
|
impl DescriptorMeta for ExtendedDescriptor {
|
2020-08-12 12:51:50 +02:00
|
|
|
fn is_witness(&self) -> bool {
|
2021-02-02 20:06:40 -05:00
|
|
|
matches!(
|
|
|
|
self.desc_type(),
|
|
|
|
DescriptorType::Wpkh
|
|
|
|
| DescriptorType::ShWpkh
|
|
|
|
| DescriptorType::Wsh
|
|
|
|
| DescriptorType::ShWsh
|
|
|
|
| DescriptorType::ShWshSortedMulti
|
|
|
|
| DescriptorType::WshSortedMulti
|
|
|
|
)
|
2020-01-27 22:02:55 +01:00
|
|
|
}
|
|
|
|
|
2022-04-14 17:20:46 +02:00
|
|
|
fn is_taproot(&self) -> bool {
|
|
|
|
self.desc_type() == DescriptorType::Tr
|
|
|
|
}
|
|
|
|
|
2023-10-16 19:51:53 +11:00
|
|
|
fn get_extended_keys(&self) -> Vec<DescriptorXKey<Xpub>> {
|
2021-02-02 20:06:40 -05:00
|
|
|
let mut answer = Vec::new();
|
2020-11-30 15:13:33 +01:00
|
|
|
|
2021-02-02 20:06:40 -05:00
|
|
|
self.for_each_key(|pk| {
|
2022-10-25 11:15:43 +02:00
|
|
|
if let DescriptorPublicKey::XPub(xpub) = pk {
|
2021-02-02 20:06:40 -05:00
|
|
|
answer.push(xpub.clone());
|
2020-08-12 12:51:50 +02:00
|
|
|
}
|
2020-07-19 19:24:05 +02:00
|
|
|
|
2021-02-02 20:06:40 -05:00
|
|
|
true
|
|
|
|
});
|
2020-08-12 12:51:50 +02:00
|
|
|
|
2022-11-03 15:59:38 +08:00
|
|
|
answer
|
2020-02-15 21:27:51 +01:00
|
|
|
}
|
|
|
|
|
2023-03-02 19:08:33 +01:00
|
|
|
fn derive_from_psbt_key_origins(
|
2021-02-02 20:06:40 -05:00
|
|
|
&self,
|
2022-04-29 12:59:09 +02:00
|
|
|
key_origins: BTreeMap<Fingerprint, (&DerivationPath, SinglePubKey)>,
|
2023-03-02 19:08:33 +01:00
|
|
|
secp: &SecpCtx,
|
2022-10-25 11:15:43 +02:00
|
|
|
) -> Option<DerivedDescriptor> {
|
2022-04-29 12:59:09 +02:00
|
|
|
// Ensure that deriving `xpub` with `path` yields `expected`
|
2023-10-16 19:51:53 +11:00
|
|
|
let verify_key =
|
|
|
|
|xpub: &DescriptorXKey<Xpub>, path: &DerivationPath, expected: &SinglePubKey| {
|
|
|
|
let derived = xpub
|
|
|
|
.xkey
|
|
|
|
.derive_pub(secp, path)
|
|
|
|
.expect("The path should never contain hardened derivation steps")
|
|
|
|
.public_key;
|
|
|
|
|
|
|
|
match expected {
|
|
|
|
SinglePubKey::FullKey(pk) if &PublicKey::new(derived) == pk => true,
|
|
|
|
SinglePubKey::XOnly(pk) if &XOnlyPublicKey::from(derived) == pk => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
};
|
2021-02-02 20:06:40 -05:00
|
|
|
|
|
|
|
let mut path_found = None;
|
2020-08-12 12:51:50 +02:00
|
|
|
|
2022-04-29 12:59:09 +02:00
|
|
|
// using `for_any_key` should make this stop as soon as we return `true`
|
|
|
|
self.for_any_key(|key| {
|
2022-10-25 11:15:43 +02:00
|
|
|
if let DescriptorPublicKey::XPub(xpub) = key {
|
2022-04-29 12:59:09 +02:00
|
|
|
// Check if the key matches one entry in our `key_origins`. If it does, `matches()` will
|
2020-08-12 12:51:50 +02:00
|
|
|
// return the "prefix" that matched, so we remove that prefix from the full path
|
2022-04-29 12:59:09 +02:00
|
|
|
// found in `key_origins` and save it in `derive_path`. We expect this to be a derivation
|
2021-02-02 20:06:40 -05:00
|
|
|
// path of length 1 if the key is `wildcard` and an empty path otherwise.
|
2020-11-16 22:07:38 +01:00
|
|
|
let root_fingerprint = xpub.root_fingerprint(secp);
|
2022-04-29 12:59:09 +02:00
|
|
|
let derive_path = key_origins
|
2020-08-12 12:51:50 +02:00
|
|
|
.get_key_value(&root_fingerprint)
|
2022-04-29 12:59:09 +02:00
|
|
|
.and_then(|(fingerprint, (path, expected))| {
|
|
|
|
xpub.matches(&(*fingerprint, (*path).clone()), secp)
|
|
|
|
.zip(Some((path, expected)))
|
2020-11-16 22:07:38 +01:00
|
|
|
})
|
2022-04-29 12:59:09 +02:00
|
|
|
.and_then(|(prefix, (full_path, expected))| {
|
|
|
|
let derive_path = full_path
|
2020-08-12 12:51:50 +02:00
|
|
|
.into_iter()
|
2020-09-29 18:18:50 +02:00
|
|
|
.skip(prefix.into_iter().count())
|
2020-08-12 12:51:50 +02:00
|
|
|
.cloned()
|
2022-04-29 12:59:09 +02:00
|
|
|
.collect::<DerivationPath>();
|
|
|
|
|
|
|
|
// `derive_path` only contains the replacement index for the wildcard, if present, or
|
|
|
|
// an empty path for fixed descriptors. To verify the key we also need the normal steps
|
|
|
|
// that come before the wildcard, so we take them directly from `xpub` and then append
|
|
|
|
// the final index
|
|
|
|
if verify_key(
|
|
|
|
xpub,
|
|
|
|
&xpub.derivation_path.extend(derive_path.clone()),
|
|
|
|
expected,
|
|
|
|
) {
|
|
|
|
Some(derive_path)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2020-08-12 12:51:50 +02:00
|
|
|
});
|
2020-10-09 12:03:47 +02:00
|
|
|
|
2022-04-29 12:59:09 +02:00
|
|
|
match derive_path {
|
2021-02-02 20:06:40 -05:00
|
|
|
Some(path) if xpub.wildcard != Wildcard::None && path.len() == 1 => {
|
|
|
|
// Ignore hardened wildcards
|
|
|
|
if let ChildNumber::Normal { index } = path[0] {
|
2022-04-29 12:59:09 +02:00
|
|
|
path_found = Some(index);
|
|
|
|
return true;
|
2021-02-02 20:06:40 -05:00
|
|
|
}
|
2020-10-09 12:03:47 +02:00
|
|
|
}
|
2021-02-02 20:06:40 -05:00
|
|
|
Some(path) if xpub.wildcard == Wildcard::None && path.is_empty() => {
|
2022-04-29 12:59:09 +02:00
|
|
|
path_found = Some(0);
|
|
|
|
return true;
|
2020-10-09 12:03:47 +02:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
2020-02-15 21:27:51 +01:00
|
|
|
}
|
|
|
|
|
2022-04-29 12:59:09 +02:00
|
|
|
false
|
2021-02-02 20:06:40 -05:00
|
|
|
});
|
2020-01-27 22:02:55 +01:00
|
|
|
|
2023-07-19 15:27:48 +02:00
|
|
|
path_found.map(|path| {
|
|
|
|
self.at_derivation_index(path)
|
|
|
|
.expect("We ignore hardened wildcards")
|
|
|
|
})
|
2020-01-27 22:02:55 +01:00
|
|
|
}
|
|
|
|
|
2023-03-02 19:08:33 +01:00
|
|
|
fn derive_from_hd_keypaths(
|
2022-04-29 12:59:09 +02:00
|
|
|
&self,
|
|
|
|
hd_keypaths: &HdKeyPaths,
|
2023-03-02 19:08:33 +01:00
|
|
|
secp: &SecpCtx,
|
2022-10-25 11:15:43 +02:00
|
|
|
) -> Option<DerivedDescriptor> {
|
2022-04-29 12:59:09 +02:00
|
|
|
// "Convert" an hd_keypaths map to the format required by `derive_from_psbt_key_origins`
|
|
|
|
let key_origins = hd_keypaths
|
|
|
|
.iter()
|
|
|
|
.map(|(pk, (fingerprint, path))| {
|
|
|
|
(
|
|
|
|
*fingerprint,
|
|
|
|
(path, SinglePubKey::FullKey(PublicKey::new(*pk))),
|
|
|
|
)
|
|
|
|
})
|
|
|
|
.collect();
|
|
|
|
self.derive_from_psbt_key_origins(key_origins, secp)
|
|
|
|
}
|
|
|
|
|
2023-03-02 19:08:33 +01:00
|
|
|
fn derive_from_tap_key_origins(
|
2022-04-29 12:59:09 +02:00
|
|
|
&self,
|
|
|
|
tap_key_origins: &TapKeyOrigins,
|
2023-03-02 19:08:33 +01:00
|
|
|
secp: &SecpCtx,
|
2022-10-25 11:15:43 +02:00
|
|
|
) -> Option<DerivedDescriptor> {
|
2022-04-29 12:59:09 +02:00
|
|
|
// "Convert" a tap_key_origins map to the format required by `derive_from_psbt_key_origins`
|
|
|
|
let key_origins = tap_key_origins
|
|
|
|
.iter()
|
|
|
|
.map(|(pk, (_, (fingerprint, path)))| (*fingerprint, (path, SinglePubKey::XOnly(*pk))))
|
|
|
|
.collect();
|
|
|
|
self.derive_from_psbt_key_origins(key_origins, secp)
|
|
|
|
}
|
|
|
|
|
2023-03-02 19:08:33 +01:00
|
|
|
fn derive_from_psbt_input(
|
2020-01-27 22:02:55 +01:00
|
|
|
&self,
|
2020-08-12 12:51:50 +02:00
|
|
|
psbt_input: &psbt::Input,
|
|
|
|
utxo: Option<TxOut>,
|
2023-03-02 19:08:33 +01:00
|
|
|
secp: &SecpCtx,
|
2022-10-25 11:15:43 +02:00
|
|
|
) -> Option<DerivedDescriptor> {
|
2021-02-02 20:06:40 -05:00
|
|
|
if let Some(derived) = self.derive_from_hd_keypaths(&psbt_input.bip32_derivation, secp) {
|
2020-08-12 12:51:50 +02:00
|
|
|
return Some(derived);
|
2021-02-02 20:06:40 -05:00
|
|
|
}
|
2022-04-29 12:59:09 +02:00
|
|
|
if let Some(derived) = self.derive_from_tap_key_origins(&psbt_input.tap_key_origins, secp) {
|
|
|
|
return Some(derived);
|
|
|
|
}
|
2022-10-25 11:15:43 +02:00
|
|
|
if self.has_wildcard() {
|
2021-02-02 20:06:40 -05:00
|
|
|
// We can't try to bruteforce the derivation index, exit here
|
2020-08-12 12:51:50 +02:00
|
|
|
return None;
|
2020-01-27 22:02:55 +01:00
|
|
|
}
|
|
|
|
|
2023-07-19 15:27:48 +02:00
|
|
|
let descriptor = self.at_derivation_index(0).expect("0 is not hardened");
|
2021-02-02 20:06:40 -05:00
|
|
|
match descriptor.desc_type() {
|
|
|
|
// TODO: add pk() here
|
2022-04-14 17:20:46 +02:00
|
|
|
DescriptorType::Pkh
|
|
|
|
| DescriptorType::Wpkh
|
|
|
|
| DescriptorType::ShWpkh
|
|
|
|
| DescriptorType::Tr
|
2020-08-12 12:51:50 +02:00
|
|
|
if utxo.is_some()
|
2021-02-02 20:06:40 -05:00
|
|
|
&& descriptor.script_pubkey() == utxo.as_ref().unwrap().script_pubkey =>
|
2020-11-16 22:07:38 +01:00
|
|
|
{
|
2021-02-02 20:06:40 -05:00
|
|
|
Some(descriptor)
|
2020-11-16 22:07:38 +01:00
|
|
|
}
|
2021-02-02 20:06:40 -05:00
|
|
|
DescriptorType::Bare | DescriptorType::Sh | DescriptorType::ShSortedMulti
|
2020-11-16 22:07:38 +01:00
|
|
|
if psbt_input.redeem_script.is_some()
|
2022-04-14 17:20:46 +02:00
|
|
|
&& &descriptor.explicit_script().unwrap()
|
2021-02-02 20:06:40 -05:00
|
|
|
== psbt_input.redeem_script.as_ref().unwrap() =>
|
2020-11-16 22:07:38 +01:00
|
|
|
{
|
2021-02-02 20:06:40 -05:00
|
|
|
Some(descriptor)
|
2020-11-16 22:07:38 +01:00
|
|
|
}
|
2021-02-02 20:06:40 -05:00
|
|
|
DescriptorType::Wsh
|
|
|
|
| DescriptorType::ShWsh
|
|
|
|
| DescriptorType::ShWshSortedMulti
|
|
|
|
| DescriptorType::WshSortedMulti
|
2020-11-16 22:07:38 +01:00
|
|
|
if psbt_input.witness_script.is_some()
|
2022-04-14 17:20:46 +02:00
|
|
|
&& &descriptor.explicit_script().unwrap()
|
2021-02-02 20:06:40 -05:00
|
|
|
== psbt_input.witness_script.as_ref().unwrap() =>
|
2020-08-12 12:51:50 +02:00
|
|
|
{
|
2021-02-02 20:06:40 -05:00
|
|
|
Some(descriptor)
|
2020-08-12 12:51:50 +02:00
|
|
|
}
|
|
|
|
_ => None,
|
2020-02-17 14:22:53 +01:00
|
|
|
}
|
|
|
|
}
|
2020-02-07 23:22:28 +01:00
|
|
|
}
|
|
|
|
|
2020-01-27 22:02:55 +01:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2023-01-10 15:10:02 +11:00
|
|
|
use alloc::string::ToString;
|
|
|
|
use core::str::FromStr;
|
2020-01-27 22:02:55 +01:00
|
|
|
|
2022-12-13 07:55:32 +10:00
|
|
|
use assert_matches::assert_matches;
|
2023-10-16 19:51:53 +11:00
|
|
|
use bitcoin::hex::FromHex;
|
2020-11-16 22:07:38 +01:00
|
|
|
use bitcoin::secp256k1::Secp256k1;
|
2023-10-16 19:51:53 +11:00
|
|
|
use bitcoin::{bip32, Psbt};
|
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(chain): use `minimal_non_dust()` instead of `dust_value()`
fix(chain): use `compute_txid()` instead of `txid`
deps(testenv): bump `electrsd` to `0.28.0`
deps(electrum): bump `electrum-client` to `0.20.0`
fix(electrum): use `compute_txid()` instead of `txid`
deps(esplora): bump `esplora-client` to `0.8.0`
deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to
`0.19.0`
fix(bitcoind_rpc): use `compute_txid()` instead of `txid`
fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected
`Signature` fields
fix(sqlite): use `compute_txid()` instead of `txid`
deps(hwi): bump `hwi` to `0.9.0`
deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(wallet): use `compute_txid()` and `minimal_non_dust()`
- update to use `compute_txid()` instead of deprecated `txid()`
- update to use `minimal_non_dust()` instead of `dust_value()`
- remove unused `bitcoin::hex::FromHex`.
fix(wallet): uses `.into` conversion on `Network` for `NetworkKind`
- uses `.into()` when appropriate, otherwise use the explicit
`NetworkKind`, and it's `.is_mainnet()` method.
fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError`
fix(wallet): fields on taproot, and ecdsa `Signature` structure
fix(wallet/wallet): convert `Weight` to `usize` for now
- converts the `bitcoin-units::Weight` type to `usize` with help of
`to_wu()` method.
- it should be updated/refactored in the future to handle the `Weight`
type throughout the code instead of current `usize`, only converting
it for now.
- allows the usage of deprecated `is_provably_unspendable()`, needs
further discussion if suggested `is_op_return` is suitable.
- update the expect field to `signature`, as it was renamed from `sig`.
fix(wallet/wallet): use `is_op_return` instead of
`is_provably_unspendable`
fix(wallet/wallet): use `relative::Locktime` instead of `Sequence`
fix(wallet/descriptor): use `ParsePublicKeyError`
fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and
`RelLockTime` to `absolute::LockTime` and `relative::LockTime`
fix(wallet/wallet): use `Message::from_digest()` instead of relying on
deprecated `ThirtyTwoByteHash` trait.
fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it
internally
fix(wallet/wallet): remove `0x` prefix from expected `TxId` display
fix(examples): use `compute_txid()` instead of `txid`
fix(ci): remove usage of `bitcoin/no-std` feature
- remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
|
|
|
use bitcoin::{NetworkKind, ScriptBuf};
|
2020-01-27 22:02:55 +01:00
|
|
|
|
2020-08-12 12:51:50 +02:00
|
|
|
use super::*;
|
2021-03-30 16:33:07 +02:00
|
|
|
use crate::psbt::PsbtUtils;
|
2020-01-27 22:02:55 +01:00
|
|
|
|
|
|
|
#[test]
|
2020-08-15 21:24:13 +02:00
|
|
|
fn test_derive_from_psbt_input_wpkh_wif() {
|
2020-08-12 12:51:50 +02:00
|
|
|
let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
|
|
|
|
"wpkh(02b4632d08485ff1df2db55b9dafd23347d1c47a457072a1e87be26896549a8737)",
|
2020-01-27 22:02:55 +01:00
|
|
|
)
|
2020-08-12 12:51:50 +02:00
|
|
|
.unwrap();
|
2023-07-19 15:27:48 +02:00
|
|
|
let psbt = Psbt::deserialize(
|
2020-08-15 21:24:13 +02:00
|
|
|
&Vec::<u8>::from_hex(
|
|
|
|
"70736274ff010052010000000162307be8e431fbaff807cdf9cdc3fde44d7402\
|
|
|
|
11bc8342c31ffd6ec11fe35bcc0100000000ffffffff01328601000000000016\
|
|
|
|
001493ce48570b55c42c2af816aeaba06cfee1224fae000000000001011fa086\
|
|
|
|
01000000000016001493ce48570b55c42c2af816aeaba06cfee1224fae010304\
|
|
|
|
010000000000",
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(descriptor
|
2020-11-16 22:07:38 +01:00
|
|
|
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
|
2020-08-15 21:24:13 +02:00
|
|
|
.is_some());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_derive_from_psbt_input_pkh_tpub() {
|
|
|
|
let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
|
|
|
|
"pkh([0f056943/44h/0h/0h]tpubDDpWvmUrPZrhSPmUzCMBHffvC3HyMAPnWDSAQNBTnj1iZeJa7BZQEttFiP4DS4GCcXQHezdXhn86Hj6LHX5EDstXPWrMaSneRWM8yUf6NFd/10/*)",
|
|
|
|
)
|
|
|
|
.unwrap();
|
2023-07-19 15:27:48 +02:00
|
|
|
let psbt = Psbt::deserialize(
|
2020-08-15 21:24:13 +02:00
|
|
|
&Vec::<u8>::from_hex(
|
|
|
|
"70736274ff010053010000000145843b86be54a3cd8c9e38444e1162676c00df\
|
|
|
|
e7964122a70df491ea12fd67090100000000ffffffff01c19598000000000017\
|
|
|
|
a91432bb94283282f72b2e034709e348c44d5a4db0ef8700000000000100f902\
|
|
|
|
0000000001010167e99c0eb67640f3a1b6805f2d8be8238c947f8aaf49eb0a9c\
|
|
|
|
bee6a42c984200000000171600142b29a22019cca05b9c2b2d283a4c4489e1cf\
|
|
|
|
9f8ffeffffff02a01dced06100000017a914e2abf033cadbd74f0f4c74946201\
|
|
|
|
decd20d5c43c8780969800000000001976a9148b0fce5fb1264e599a65387313\
|
|
|
|
3c95478b902eb288ac02473044022015d9211576163fa5b001e84dfa3d44efd9\
|
|
|
|
86b8f3a0d3d2174369288b2b750906022048dacc0e5d73ae42512fd2b97e2071\
|
|
|
|
a8d0bce443b390b1fe0b8128fe70ec919e01210232dad1c5a67dcb0116d407e2\
|
|
|
|
52584228ab7ec00e8b9779d0c3ffe8114fc1a7d2c80600000103040100000022\
|
|
|
|
0603433b83583f8c4879b329dd08bbc7da935e4cc02f637ff746e05f0466ffb2\
|
|
|
|
a6a2180f0569432c00008000000080000000800a000000000000000000",
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(descriptor
|
2020-11-16 22:07:38 +01:00
|
|
|
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
|
2020-08-15 21:24:13 +02:00
|
|
|
.is_some());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_derive_from_psbt_input_wsh() {
|
|
|
|
let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
|
|
|
|
"wsh(and_v(v:pk(03b6633fef2397a0a9de9d7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14),older(6)))",
|
|
|
|
)
|
|
|
|
.unwrap();
|
2023-07-19 15:27:48 +02:00
|
|
|
let psbt = Psbt::deserialize(
|
2020-08-15 21:24:13 +02:00
|
|
|
&Vec::<u8>::from_hex(
|
|
|
|
"70736274ff01005302000000011c8116eea34408ab6529223c9a176606742207\
|
|
|
|
67a1ff1d46a6e3c4a88243ea6e01000000000600000001109698000000000017\
|
|
|
|
a914ad105f61102e0d01d7af40d06d6a5c3ae2f7fde387000000000001012b80\
|
|
|
|
969800000000002200203ca72f106a72234754890ca7640c43f65d2174e44d33\
|
|
|
|
336030f9059345091044010304010000000105252103b6633fef2397a0a9de9d\
|
|
|
|
7b6f23aef8368a6e362b0581f0f0af70d5ecfd254b14ad56b20000",
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
assert!(descriptor
|
2020-11-16 22:07:38 +01:00
|
|
|
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
|
2020-08-15 21:24:13 +02:00
|
|
|
.is_some());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_derive_from_psbt_input_sh() {
|
|
|
|
let descriptor = Descriptor::<DescriptorPublicKey>::from_str(
|
|
|
|
"sh(and_v(v:pk(021403881a5587297818fcaf17d239cefca22fce84a45b3b1d23e836c4af671dbb),after(630000)))",
|
|
|
|
)
|
|
|
|
.unwrap();
|
2023-07-19 15:27:48 +02:00
|
|
|
let psbt = Psbt::deserialize(
|
2020-08-15 21:24:13 +02:00
|
|
|
&Vec::<u8>::from_hex(
|
|
|
|
"70736274ff0100530100000001bc8c13df445dfadcc42afa6dc841f85d22b01d\
|
|
|
|
a6270ebf981740f4b7b1d800390000000000feffffff01ba9598000000000017\
|
|
|
|
a91457b148ba4d3e5fa8608a8657875124e3d1c9390887f09c0900000100e002\
|
|
|
|
0000000001016ba1bbe05cc93574a0d611ec7d93ad0ab6685b28d0cd80e8a82d\
|
|
|
|
debb326643c90100000000feffffff02809698000000000017a914d9a6e8c455\
|
|
|
|
8e16c8253afe53ce37ad61cf4c38c487403504cf6100000017a9144044fb6e0b\
|
|
|
|
757dfc1b34886b6a95aef4d3db137e870247304402202a9b72d939bcde8ba2a1\
|
|
|
|
e0980597e47af4f5c152a78499143c3d0a78ac2286a602207a45b1df9e93b8c9\
|
|
|
|
6f09f5c025fe3e413ca4b905fe65ee55d32a3276439a9b8f012102dc1fcc2636\
|
|
|
|
4da1aa718f03d8d9bd6f2ff410ed2cf1245a168aa3bcc995ac18e0a806000001\
|
|
|
|
03040100000001042821021403881a5587297818fcaf17d239cefca22fce84a4\
|
|
|
|
5b3b1d23e836c4af671dbbad03f09c09b10000",
|
|
|
|
)
|
|
|
|
.unwrap(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
2020-08-12 12:51:50 +02:00
|
|
|
|
2020-08-15 21:24:13 +02:00
|
|
|
assert!(descriptor
|
2020-11-16 22:07:38 +01:00
|
|
|
.derive_from_psbt_input(&psbt.inputs[0], psbt.get_utxo_for(0), &Secp256k1::new())
|
2020-08-15 21:24:13 +02:00
|
|
|
.is_some());
|
2020-01-27 22:02:55 +01:00
|
|
|
}
|
2020-09-21 15:44:07 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_to_wallet_descriptor_fixup_networks() {
|
2021-02-12 23:02:13 -08:00
|
|
|
use crate::keys::{any_network, IntoDescriptorKey};
|
2020-09-21 15:44:07 +02:00
|
|
|
|
2021-02-02 20:06:40 -05:00
|
|
|
let secp = Secp256k1::new();
|
|
|
|
|
2023-10-16 19:51:53 +11:00
|
|
|
let xprv = bip32::Xpriv::from_str("xprv9s21ZrQH143K3c3gF1DUWpWNr2SG2XrG8oYPpqYh7hoWsJy9NjabErnzriJPpnGHyKz5NgdXmq1KVbqS1r4NXdCoKitWg5e86zqXHa8kxyB").unwrap();
|
2020-09-21 15:44:07 +02:00
|
|
|
let path = bip32::DerivationPath::from_str("m/0").unwrap();
|
|
|
|
|
|
|
|
// here `to_descriptor_key` will set the valid networks for the key to only mainnet, since
|
|
|
|
// we are using an "xpub"
|
2022-11-23 11:22:03 +01:00
|
|
|
let key = (xprv, path.clone()).into_descriptor_key().unwrap();
|
2020-09-21 15:44:07 +02:00
|
|
|
// override it with any. this happens in some key conversions, like bip39
|
|
|
|
let key = key.override_valid_networks(any_network());
|
|
|
|
|
|
|
|
// make a descriptor out of it
|
|
|
|
let desc = crate::descriptor!(wpkh(key)).unwrap();
|
2021-11-23 13:40:58 -05:00
|
|
|
// this should convert the key that supports "any_network" to the right network (testnet)
|
2022-11-23 11:22:03 +01:00
|
|
|
let (wallet_desc, keymap) = desc
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet)
|
|
|
|
.unwrap();
|
2020-09-21 15:44:07 +02:00
|
|
|
|
2022-11-23 11:22:03 +01:00
|
|
|
let mut xprv_testnet = xprv;
|
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(chain): use `minimal_non_dust()` instead of `dust_value()`
fix(chain): use `compute_txid()` instead of `txid`
deps(testenv): bump `electrsd` to `0.28.0`
deps(electrum): bump `electrum-client` to `0.20.0`
fix(electrum): use `compute_txid()` instead of `txid`
deps(esplora): bump `esplora-client` to `0.8.0`
deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to
`0.19.0`
fix(bitcoind_rpc): use `compute_txid()` instead of `txid`
fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected
`Signature` fields
fix(sqlite): use `compute_txid()` instead of `txid`
deps(hwi): bump `hwi` to `0.9.0`
deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(wallet): use `compute_txid()` and `minimal_non_dust()`
- update to use `compute_txid()` instead of deprecated `txid()`
- update to use `minimal_non_dust()` instead of `dust_value()`
- remove unused `bitcoin::hex::FromHex`.
fix(wallet): uses `.into` conversion on `Network` for `NetworkKind`
- uses `.into()` when appropriate, otherwise use the explicit
`NetworkKind`, and it's `.is_mainnet()` method.
fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError`
fix(wallet): fields on taproot, and ecdsa `Signature` structure
fix(wallet/wallet): convert `Weight` to `usize` for now
- converts the `bitcoin-units::Weight` type to `usize` with help of
`to_wu()` method.
- it should be updated/refactored in the future to handle the `Weight`
type throughout the code instead of current `usize`, only converting
it for now.
- allows the usage of deprecated `is_provably_unspendable()`, needs
further discussion if suggested `is_op_return` is suitable.
- update the expect field to `signature`, as it was renamed from `sig`.
fix(wallet/wallet): use `is_op_return` instead of
`is_provably_unspendable`
fix(wallet/wallet): use `relative::Locktime` instead of `Sequence`
fix(wallet/descriptor): use `ParsePublicKeyError`
fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and
`RelLockTime` to `absolute::LockTime` and `relative::LockTime`
fix(wallet/wallet): use `Message::from_digest()` instead of relying on
deprecated `ThirtyTwoByteHash` trait.
fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it
internally
fix(wallet/wallet): remove `0x` prefix from expected `TxId` display
fix(examples): use `compute_txid()` instead of `txid`
fix(ci): remove usage of `bitcoin/no-std` feature
- remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
|
|
|
xprv_testnet.network = NetworkKind::Test;
|
2022-11-23 11:22:03 +01:00
|
|
|
|
2023-10-16 19:51:53 +11:00
|
|
|
let xpub_testnet = bip32::Xpub::from_priv(&secp, &xprv_testnet);
|
2022-11-23 11:22:03 +01:00
|
|
|
let desc_pubkey = DescriptorPublicKey::XPub(DescriptorXKey {
|
|
|
|
xkey: xpub_testnet,
|
|
|
|
origin: None,
|
|
|
|
derivation_path: path,
|
|
|
|
wildcard: Wildcard::Unhardened,
|
|
|
|
});
|
|
|
|
|
|
|
|
assert_eq!(wallet_desc.to_string(), "wpkh(tpubD6NzVbkrYhZ4XtJzoDja5snUjBNQRP5B3f4Hyn1T1x6PVPxzzVjvw6nJx2D8RBCxog9GEVjZoyStfepTz7TtKoBVdkCtnc7VCJh9dD4RAU9/0/*)#a3svx0ha");
|
|
|
|
assert_eq!(
|
|
|
|
keymap
|
|
|
|
.get(&desc_pubkey)
|
|
|
|
.map(|key| key.to_public(&secp).unwrap()),
|
|
|
|
Some(desc_pubkey)
|
|
|
|
);
|
2020-09-21 15:44:07 +02:00
|
|
|
}
|
2020-09-25 22:21:11 -07:00
|
|
|
|
2021-02-12 22:34:43 -08:00
|
|
|
// test IntoWalletDescriptor trait from &str with and without checksum appended
|
2020-09-25 22:21:11 -07:00
|
|
|
#[test]
|
|
|
|
fn test_descriptor_from_str_with_checksum() {
|
2021-02-02 20:06:40 -05:00
|
|
|
let secp = Secp256k1::new();
|
|
|
|
|
2020-09-25 22:21:11 -07:00
|
|
|
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#tqz0nc62"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)#67ju93jw"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet);
|
2022-12-13 07:55:32 +10:00
|
|
|
assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
|
2020-09-25 22:21:11 -07:00
|
|
|
|
|
|
|
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet);
|
2022-12-13 07:55:32 +10:00
|
|
|
assert_matches!(desc, Err(DescriptorError::InvalidDescriptorChecksum));
|
2020-09-25 22:21:11 -07:00
|
|
|
}
|
|
|
|
|
2021-02-12 22:34:43 -08:00
|
|
|
// test IntoWalletDescriptor trait from &str with keys from right and wrong network
|
2020-09-25 22:21:11 -07:00
|
|
|
#[test]
|
|
|
|
fn test_descriptor_from_str_with_keys_network() {
|
2021-02-02 20:06:40 -05:00
|
|
|
let secp = Secp256k1::new();
|
|
|
|
|
2020-09-25 22:21:11 -07:00
|
|
|
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Regtest);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Regtest);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "sh(wpkh(02864bb4ad00cefa806098a69e192bbda937494e69eb452b87bb3f20f6283baedb))"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "sh(wpkh(02864bb4ad00cefa806098a69e192bbda937494e69eb452b87bb3f20f6283baedb))"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Bitcoin);
|
2020-09-25 22:21:11 -07:00
|
|
|
assert!(desc.is_ok());
|
|
|
|
|
|
|
|
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Bitcoin);
|
2022-12-13 07:55:32 +10:00
|
|
|
assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
|
2020-09-25 22:21:11 -07:00
|
|
|
|
|
|
|
let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Bitcoin);
|
2022-12-13 07:55:32 +10:00
|
|
|
assert_matches!(desc, Err(DescriptorError::Key(KeyError::InvalidNetwork)));
|
2020-09-25 22:21:11 -07:00
|
|
|
}
|
|
|
|
|
2021-02-12 22:34:43 -08:00
|
|
|
// test IntoWalletDescriptor trait from the output of the descriptor!() macro
|
2020-09-25 22:21:11 -07:00
|
|
|
#[test]
|
|
|
|
fn test_descriptor_from_str_from_output_of_macro() {
|
2021-02-02 20:06:40 -05:00
|
|
|
let secp = Secp256k1::new();
|
|
|
|
|
2023-10-16 19:51:53 +11:00
|
|
|
let tpub = bip32::Xpub::from_str("tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK").unwrap();
|
2020-09-25 22:21:11 -07:00
|
|
|
let path = bip32::DerivationPath::from_str("m/1/2").unwrap();
|
2021-02-11 11:00:48 -08:00
|
|
|
let key = (tpub, path).into_descriptor_key().unwrap();
|
2020-09-25 22:21:11 -07:00
|
|
|
|
|
|
|
// make a descriptor out of it
|
|
|
|
let desc = crate::descriptor!(wpkh(key)).unwrap();
|
|
|
|
|
2021-02-11 11:00:48 -08:00
|
|
|
let (wallet_desc, _) = desc
|
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet)
|
|
|
|
.unwrap();
|
2020-09-25 22:21:11 -07:00
|
|
|
let wallet_desc_str = wallet_desc.to_string();
|
2021-02-02 20:06:40 -05:00
|
|
|
assert_eq!(wallet_desc_str, "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)#67ju93jw");
|
2020-09-25 22:21:11 -07:00
|
|
|
|
|
|
|
let (wallet_desc2, _) = wallet_desc_str
|
2021-02-11 11:00:48 -08:00
|
|
|
.into_wallet_descriptor(&secp, Network::Testnet)
|
2020-09-25 22:21:11 -07:00
|
|
|
.unwrap();
|
|
|
|
assert_eq!(wallet_desc, wallet_desc2)
|
|
|
|
}
|
2021-02-15 11:33:47 -05:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_into_wallet_descriptor_checked() {
|
|
|
|
let secp = Secp256k1::new();
|
|
|
|
|
|
|
|
let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0'/1/2/*)";
|
|
|
|
let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
|
|
|
|
|
2022-12-13 07:55:32 +10:00
|
|
|
assert_matches!(result, Err(DescriptorError::HardenedDerivationXpub));
|
2021-02-23 15:57:43 +01:00
|
|
|
|
2023-07-19 18:48:20 +02:00
|
|
|
let descriptor = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/<0;1>/*)";
|
|
|
|
let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
|
|
|
|
|
|
|
|
assert_matches!(result, Err(DescriptorError::MultiPath));
|
|
|
|
|
|
|
|
// repeated pubkeys
|
2022-09-24 13:48:05 +02:00
|
|
|
let descriptor = "wsh(multi(2,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*,tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/0/*))";
|
2021-02-23 15:57:43 +01:00
|
|
|
let result = into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet);
|
|
|
|
|
|
|
|
assert!(result.is_err());
|
2021-02-15 11:33:47 -05:00
|
|
|
}
|
2022-05-23 21:01:28 +02:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_sh_wsh_sortedmulti_redeemscript() {
|
2022-10-25 11:15:43 +02:00
|
|
|
use miniscript::psbt::PsbtInputExt;
|
2022-05-23 21:01:28 +02:00
|
|
|
|
|
|
|
let secp = Secp256k1::new();
|
|
|
|
|
|
|
|
let descriptor = "sh(wsh(sortedmulti(3,tpubDEsqS36T4DVsKJd9UH8pAKzrkGBYPLEt9jZMwpKtzh1G6mgYehfHt9WCgk7MJG5QGSFWf176KaBNoXbcuFcuadAFKxDpUdMDKGBha7bY3QM/0/*,tpubDF3cpwfs7fMvXXuoQbohXtLjNM6ehwYT287LWtmLsd4r77YLg6MZg4vTETx5MSJ2zkfigbYWu31VA2Z2Vc1cZugCYXgS7FQu6pE8V6TriEH/0/*,tpubDE1SKfcW76Tb2AASv5bQWMuScYNAdoqLHoexw13sNDXwmUhQDBbCD3QAedKGLhxMrWQdMDKENzYtnXPDRvexQPNuDrLj52wAjHhNEm8sJ4p/0/*,tpubDFLc6oXwJmhm3FGGzXkfJNTh2KitoY3WhmmQvuAjMhD8YbyWn5mAqckbxXfm2etM3p5J6JoTpSrMqRSTfMLtNW46poDaEZJ1kjd3csRSjwH/0/*,tpubDEWD9NBeWP59xXmdqSNt4VYdtTGwbpyP8WS962BuqpQeMZmX9Pur14dhXdZT5a7wR1pK6dPtZ9fP5WR493hPzemnBvkfLLYxnUjAKj1JCQV/0/*,tpubDEHyZkkwd7gZWCTgQuYQ9C4myF2hMEmyHsBCCmLssGqoqUxeT3gzohF5uEVURkf9TtmeepJgkSUmteac38FwZqirjApzNX59XSHLcwaTZCH/0/*,tpubDEqLouCekwnMUWN486kxGzD44qVgeyuqHyxUypNEiQt5RnUZNJe386TKPK99fqRV1vRkZjYAjtXGTECz98MCsdLcnkM67U6KdYRzVubeCgZ/0/*)))";
|
|
|
|
let (descriptor, _) =
|
|
|
|
into_wallet_descriptor_checked(descriptor, &secp, Network::Testnet).unwrap();
|
|
|
|
|
2023-07-19 15:27:48 +02:00
|
|
|
let descriptor = descriptor.at_derivation_index(0).unwrap();
|
2022-05-23 21:01:28 +02:00
|
|
|
|
2023-07-19 15:27:48 +02:00
|
|
|
let script = ScriptBuf::from_hex("5321022f533b667e2ea3b36e21961c9fe9dca340fbe0af5210173a83ae0337ab20a57621026bb53a98e810bd0ee61a0ed1164ba6c024786d76554e793e202dc6ce9c78c4ea2102d5b8a7d66a41ffdb6f4c53d61994022e886b4f45001fb158b95c9164d45f8ca3210324b75eead2c1f9c60e8adeb5e7009fec7a29afcdb30d829d82d09562fe8bae8521032d34f8932200833487bd294aa219dcbe000b9f9b3d824799541430009f0fa55121037468f8ea99b6c64788398b5ad25480cad08f4b0d65be54ce3a55fd206b5ae4722103f72d3d96663b0ea99b0aeb0d7f273cab11a8de37885f1dddc8d9112adb87169357ae").unwrap();
|
2022-05-23 21:01:28 +02:00
|
|
|
|
2022-10-25 11:15:43 +02:00
|
|
|
let mut psbt_input = psbt::Input::default();
|
|
|
|
psbt_input
|
|
|
|
.update_with_descriptor_unchecked(&descriptor)
|
|
|
|
.unwrap();
|
|
|
|
|
2023-10-16 19:51:53 +11:00
|
|
|
assert_eq!(psbt_input.redeem_script, Some(script.to_p2wsh()));
|
2022-10-25 11:15:43 +02:00
|
|
|
assert_eq!(psbt_input.witness_script, Some(script));
|
2022-05-23 21:01:28 +02:00
|
|
|
}
|
2020-01-27 22:02:55 +01:00
|
|
|
}
|