Use the descriptor newtype instead of the raw miniscript Descriptor type

This commit is contained in:
Antoine Poinsot 2022-08-18 11:39:16 +02:00
parent 44eb0fad9b
commit 0fd57db8a8
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
10 changed files with 112 additions and 84 deletions

View File

@ -1,7 +1,7 @@
///! Implementation of the Bitcoin interface using bitcoind.
///!
///! We use the RPC interface and a watchonly descriptor wallet.
use crate::{bitcoin::BlockChainTip, config};
use crate::{bitcoin::BlockChainTip, config, descriptors::InheritanceDescriptor};
use std::{collections::HashSet, convert::TryInto, fs, io, str::FromStr, time::Duration};
@ -10,7 +10,7 @@ use jsonrpc::{
client::Client,
simple_http::{self, SimpleHttpTransport},
};
use miniscript::{bitcoin, Descriptor, DescriptorPublicKey};
use miniscript::bitcoin;
use serde_json::Value as Json;
@ -354,7 +354,7 @@ impl BitcoinD {
}
// TODO: rescan feature will probably need another timestamp than 'now'
fn import_descriptor(&self, descriptor: &Descriptor<DescriptorPublicKey>) -> Option<String> {
fn import_descriptor(&self, descriptor: &InheritanceDescriptor) -> Option<String> {
let descriptors = vec![serde_json::json!({
"desc": descriptor.to_string(),
"timestamp": "now",
@ -400,7 +400,7 @@ impl BitcoinD {
/// Create the watchonly wallet on bitcoind, and import it the main descriptor.
pub fn create_watchonly_wallet(
&self,
main_descriptor: &Descriptor<DescriptorPublicKey>,
main_descriptor: &InheritanceDescriptor,
) -> Result<(), BitcoindError> {
// Remove any leftover. This can happen if we delete the watchonly wallet but don't restart
// bitcoind.
@ -440,7 +440,7 @@ impl BitcoinD {
/// Perform various sanity checks on the bitcoind instance.
pub fn sanity_check(
&self,
main_descriptor: &Descriptor<DescriptorPublicKey>,
main_descriptor: &InheritanceDescriptor,
config_network: bitcoin::Network,
) -> Result<(), BitcoindError> {
// Check the minimum supported bitcoind version

View File

@ -7,15 +7,11 @@ mod utils;
use crate::{
bitcoin::BitcoinInterface,
database::{Coin, DatabaseInterface},
DaemonControl, VERSION,
descriptors, DaemonControl, VERSION,
};
use utils::{deser_amount_from_sats, ser_amount};
use miniscript::{
bitcoin,
descriptor::{self, DescriptorTrait},
TranslatePk2,
};
use miniscript::bitcoin;
use serde::{Deserialize, Serialize};
impl DaemonControl {
@ -37,17 +33,13 @@ impl DaemonControl {
pub fn get_new_address(&self) -> GetAddressResult {
let mut db_conn = self.db.connection();
let index = db_conn.derivation_index();
// TODO: handle should we wrap around instead of failing?
// TODO: should we wrap around instead of failing?
db_conn.increment_derivation_index(&self.secp);
let address = self
.config
.main_descriptor
// TODO: have a descriptor newtype along with a derived descriptor one.
.derive(index.into())
.translate_pk2(|xpk| xpk.derive_public_key(&self.secp))
.expect("All pubkeys were derived, no wildcard.")
.address(self.config.bitcoin_config.network)
.expect("It's a wsh() descriptor");
.derive(index.into(), &self.secp)
.address(self.config.bitcoin_config.network);
GetAddressResult { address }
}
@ -78,7 +70,7 @@ impl DaemonControl {
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetInfoDescriptors {
pub main: descriptor::Descriptor<descriptor::DescriptorPublicKey>,
pub main: descriptors::InheritanceDescriptor,
}
/// Information about the daemon

View File

@ -1,10 +1,8 @@
use crate::descriptors::InheritanceDescriptor;
use std::{net::SocketAddr, path::PathBuf, str::FromStr, time::Duration};
use miniscript::{
bitcoin::Network,
descriptor::{Descriptor, DescriptorPublicKey},
ForEach, ForEachKey,
};
use miniscript::{bitcoin::Network, DescriptorPublicKey, ForEach, ForEachKey};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
@ -94,7 +92,7 @@ pub struct Config {
deserialize_with = "deserialize_fromstr",
serialize_with = "serialize_to_string"
)]
pub main_descriptor: Descriptor<DescriptorPublicKey>,
pub main_descriptor: InheritanceDescriptor,
/// Settings for the Bitcoin interface
pub bitcoin_config: BitcoinConfig,
/// Settings specific to bitcoind as the Bitcoin interface
@ -115,7 +113,7 @@ pub enum ConfigError {
DatadirNotFound,
FileNotFound,
ReadingFile(String),
UnexpectedDescriptor(Descriptor<DescriptorPublicKey>),
UnexpectedDescriptor(InheritanceDescriptor),
Unexpected(String),
}
@ -205,7 +203,7 @@ impl Config {
Network::Bitcoin => Network::Bitcoin,
_ => Network::Testnet,
};
let unexpected_net = self.main_descriptor.for_each_key(|pkpkh| {
let unexpected_net = self.main_descriptor.as_inner().for_each_key(|pkpkh| {
let xpub = match pkpkh {
// For DescriptorPublicKey, Pk::Hash == Self.
ForEach::Key(xpub) => xpub,
@ -242,7 +240,7 @@ mod tests {
data_dir = "/home/wizardsardine/custom/folder/"
daemon = false
log_level = "debug"
main_descriptor = "wsh(andor(thresh(1,pk(xpub6BaZSKgpaVvibu2k78QsqeDWXp92xLHZxiu1WoqLB9hKhsBf3miBUDX7PJLgSPvkj66ThVHTqdnbXpeu8crXFmDUd4HeM4s4miQS2xsv3Qb/*)),and_v(v:multi(2,03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a,0295e7f5d12a2061f1fd2286cefec592dff656a19f55f4f01305d6aa56630880ce),older(4)),thresh(2,pkh(xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU/*),a:pkh(xpub6AaffFGfH6WXfm6pwWzmUMuECQnoLeB3agMKaLyEBZ5ZVfwtnS5VJKqXBt8o5ooCWVy2H87GsZshp7DeKE25eWLyd1Ccuh2ZubQUkgpiVux/*))))#532k8uvf"
main_descriptor = "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/*)))#y5wcna2d"
[bitcoin_config]
network = "bitcoin"
@ -259,7 +257,7 @@ mod tests {
data_dir = '/home/wizardsardine/custom/folder/'
daemon = false
log_level = 'TRACE'
main_descriptor = 'wsh(andor(thresh(1,pk(xpub6BaZSKgpaVvibu2k78QsqeDWXp92xLHZxiu1WoqLB9hKhsBf3miBUDX7PJLgSPvkj66ThVHTqdnbXpeu8crXFmDUd4HeM4s4miQS2xsv3Qb/*)),and_v(v:multi(2,03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a,0295e7f5d12a2061f1fd2286cefec592dff656a19f55f4f01305d6aa56630880ce),older(4)),thresh(2,pkh(xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU/*),a:pkh(xpub6AaffFGfH6WXfm6pwWzmUMuECQnoLeB3agMKaLyEBZ5ZVfwtnS5VJKqXBt8o5ooCWVy2H87GsZshp7DeKE25eWLyd1Ccuh2ZubQUkgpiVux/*))))#532k8uvf'
main_descriptor = 'wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/*)))#y5wcna2d'
[bitcoin_config]
network = 'bitcoin'
@ -280,8 +278,7 @@ mod tests {
log_level = "trace"
data_dir = "/home/wizardsardine/custom/folder/"
# The main descriptor semantics aren't checked, yet.
main_descriptor = "wsh(andor(thresh(1,pk(xpub6BaZSKgpaVvibu2k78QsqeDWXp92xLHZxiu1WoqLB9hKhsBf3miBUDX7PJLgSPvkj66ThVHTqdnbXpeu8crXFmDUd4HeM4s4miQS2xsv3Qb/*)),and_v(v:multi(2,03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a,0295e7f5d12a2061f1fd2286cefec592dff656a19f55f4f01305d6aa56630880ce),older(4)),thresh(2,pkh(xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU/*),a:pkh(xpub6AaffFGfH6WXfm6pwWzmUMuECQnoLeB3agMKaLyEBZ5ZVfwtnS5VJKqXBt8o5ooCWVy2H87GsZshp7DeKE25eWLyd1Ccuh2ZubQUkgpiVux/*))))#532k88vf"
main_descriptor = "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/*)))#y5wcna2e"
[bitcoin_config]
network = "bitcoin"
@ -301,7 +298,7 @@ mod tests {
data_dir = "/home/wizardsardine/custom/folder/"
# The main descriptor semantics aren't checked, yet.
main_descriptor = "wsh(andor(thresh(1,pk(xpub6BaZSKgpaVvibu2k78QsqeDWXp92xLHZxiu1WoqLB9hKhsBf3miBUDX7PJLgSPvkj66ThVHTqdnbXpeu8crXFmDUd4HeM4s4miQS2xsv3Qb/*)),and_v(v:multi(2,03b506a1dbe57b4bf48c95e0c7d417b87dd3b4349d290d2e7e9ba72c912652d80a,0295e7f5d12a2061f1fd2286cefec592dff656a19f55f4f01305d6aa56630880ce),older(4)),thresh(2,pkh(xpub6AHA9hZDN11k2ijHMeS5QqHx2KP9aMBRhTDqANMnwVtdyw2TDYRmF8PjpvwUFcL1Et8Hj59S3gTSMcUQ5gAqTz3Wd8EsMTmF3DChhqPQBnU/*),a:pkh(xpub6AaffFGfH6WXfm6pwWzmUMuECQnoLeB3agMKaLyEBZ5ZVfwtnS5VJKqXBt8o5ooCWVy2H87GsZshp7DeKE25eWLyd1Ccuh2ZubQUkgpiVux/*))))#532k8uvf"
main_descriptor = ""
[bitcoin_config]
poll_interval_secs = 18

View File

@ -18,14 +18,12 @@ use crate::{
},
Coin,
},
descriptors::InheritanceDescriptor,
};
use std::{convert::TryInto, fmt, io, path};
use miniscript::{
bitcoin::{self, secp256k1},
Descriptor, DescriptorPublicKey, DescriptorTrait, TranslatePk2,
};
use miniscript::bitcoin::{self, secp256k1};
const DB_VERSION: i64 = 0;
@ -35,7 +33,7 @@ pub enum SqliteDbError {
FileNotFound(path::PathBuf),
UnsupportedVersion(i64),
InvalidNetwork(bitcoin::Network),
DescriptorMismatch(Descriptor<DescriptorPublicKey>),
DescriptorMismatch(InheritanceDescriptor),
Rusqlite(rusqlite::Error),
}
@ -79,7 +77,7 @@ impl From<rusqlite::Error> for SqliteDbError {
#[derive(Debug, Clone)]
pub struct FreshDbOptions {
pub bitcoind_network: bitcoin::Network,
pub main_descriptor: Descriptor<DescriptorPublicKey>,
pub main_descriptor: InheritanceDescriptor,
}
#[derive(Debug, Clone)]
@ -118,7 +116,7 @@ impl SqliteDb {
pub fn sanity_check(
&self,
bitcoind_network: bitcoin::Network,
main_descriptor: &Descriptor<DescriptorPublicKey>,
main_descriptor: &InheritanceDescriptor,
) -> Result<(), SqliteDbError> {
let mut conn = self.connection()?;
@ -239,11 +237,8 @@ impl SqliteConn {
let next_la_index = next_index + LOOK_AHEAD_LIMIT - 1;
let next_la_address = db_wallet
.main_descriptor
.derive(next_la_index)
.translate_pk2(|xpk| xpk.derive_public_key(secp))
.expect("All pubkeys were derived, no wildcard.")
.address(network)
.expect("It's a wsh() descriptor");
.derive(next_la_index.into(), &secp)
.address(network);
db_tx
.execute(
"INSERT INTO addresses (address, derivation_index) VALUES (?1, ?2)",
@ -344,11 +339,10 @@ mod tests {
use std::{collections::HashSet, fs, path, str::FromStr};
use bitcoin::{hashes::Hash, util::bip32};
use miniscript::{DescriptorTrait, TranslatePk2};
fn dummy_options() -> FreshDbOptions {
let desc_str = "wsh(andor(pk(tpubDEN9WSToTyy9ZQfaYqSKfmVqmq1VVLNtYfj3Vkqh67et57eJ5sTKZQBkHqSwPUsoSskJeaYnPttHe2VrkCsKA27kUaN9SDc5zhqeLzKa1rr/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/*)))#y5wcna2d";
let main_descriptor = Descriptor::<DescriptorPublicKey>::from_str(desc_str).unwrap();
let main_descriptor = InheritanceDescriptor::from_str(desc_str).unwrap();
FreshDbOptions {
bitcoind_network: bitcoin::Network::Bitcoin,
main_descriptor,
@ -396,8 +390,8 @@ mod tests {
.to_string()
.contains("Database was created for network");
fs::remove_file(&db_path).unwrap();
let other_desc_str = "wsh(andor(pk(037a27a76ebf33594c785e4fa41607860a960bb5aa3039654297b05bff57e4f9a9),older(10000),pk(0295e7f5d12a2061f1fd2286cefec592dff656a19f55f4f01305d6aa56630880ce)))";
let other_desc = Descriptor::<DescriptorPublicKey>::from_str(other_desc_str).unwrap();
let other_desc_str = "wsh(andor(pk(tpubDExU4YLJkyQ9RRbVScQq2brFxWWha7WmAUByPWyaWYwmcTv3Shx8aHp6mVwuE5n4TeM4z5DTWGf2YhNPmXtfvyr8cUDVvA3txdrFnFgNdF7/*),older(10000),pk(tpubD8LYfn6njiA2inCoxwM7EuN3cuLVcaHAwLYeups13dpevd3nHLRdK9NdQksWXrhLQVxcUZRpnp5CkJ1FhE61WRAsHxDNAkvGkoQkAeWDYjV/*)))";
let other_desc = InheritanceDescriptor::from_str(other_desc_str).unwrap();
let db = SqliteDb::new(db_path.clone(), Some(options.clone()), &secp).unwrap();
db.sanity_check(bitcoin::Network::Bitcoin, &other_desc)
.unwrap_err()
@ -522,33 +516,24 @@ mod tests {
// There is the index for the first index
let addr = options
.main_descriptor
.derive(0)
.translate_pk2(|xpk| xpk.derive_public_key(&secp))
.expect("All pubkeys were derived, no wildcard.")
.address(options.bitcoind_network)
.expect("Always a P2WSH address");
.derive(0.into(), &secp)
.address(options.bitcoind_network);
let db_addr = conn.db_address(&addr).unwrap();
assert_eq!(db_addr.derivation_index, 0.into());
// There is the index for the 199th index (look-ahead limit)
let addr = options
.main_descriptor
.derive(199)
.translate_pk2(|xpk| xpk.derive_public_key(&secp))
.expect("All pubkeys were derived, no wildcard.")
.address(options.bitcoind_network)
.expect("Always a P2WSH address");
.derive(199.into(), &secp)
.address(options.bitcoind_network);
let db_addr = conn.db_address(&addr).unwrap();
assert_eq!(db_addr.derivation_index, 199.into());
// And not for the 200th one.
let addr = options
.main_descriptor
.derive(200)
.translate_pk2(|xpk| xpk.derive_public_key(&secp))
.expect("All pubkeys were derived, no wildcard.")
.address(options.bitcoind_network)
.expect("Always a P2WSH address");
.derive(200.into(), &secp)
.address(options.bitcoind_network);
assert!(conn.db_address(&addr).is_none());
// But if we increment the deposit derivation index, the 200th one will be there.

View File

@ -1,9 +1,8 @@
use crate::descriptors::InheritanceDescriptor;
use std::{convert::TryFrom, str::FromStr};
use miniscript::{
bitcoin::{self, consensus::encode, util::bip32},
Descriptor, DescriptorPublicKey,
};
use miniscript::bitcoin::{self, consensus::encode, util::bip32};
pub const SCHEMA: &str = "\
CREATE TABLE version (
@ -86,7 +85,7 @@ impl TryFrom<&rusqlite::Row<'_>> for DbTip {
pub struct DbWallet {
pub id: i64,
pub timestamp: u32,
pub main_descriptor: Descriptor<DescriptorPublicKey>,
pub main_descriptor: InheritanceDescriptor,
pub deposit_derivation_index: bip32::ChildNumber,
}
@ -98,7 +97,7 @@ impl TryFrom<&rusqlite::Row<'_>> for DbWallet {
let timestamp = row.get(1)?;
let desc_str: String = row.get(2)?;
let main_descriptor = Descriptor::<DescriptorPublicKey>::from_str(&desc_str)
let main_descriptor = InheritanceDescriptor::from_str(&desc_str)
.expect("Insane database: can't parse deposit descriptor");
let der_idx: u32 = row.get(3)?;

View File

@ -2,7 +2,7 @@ use crate::database::sqlite::{schema::SCHEMA, FreshDbOptions, SqliteDbError, DB_
use std::{convert::TryInto, fs, path, time};
use miniscript::{bitcoin::secp256k1, DescriptorTrait, TranslatePk2};
use miniscript::bitcoin::secp256k1;
pub const LOOK_AHEAD_LIMIT: u32 = 200;
@ -106,11 +106,8 @@ pub fn create_fresh_db(
// TODO: have this as a helper in descriptors.rs
let address = options
.main_descriptor
.derive(index)
.translate_pk2(|xpk| xpk.derive_public_key(secp))
.expect("All pubkeys were derived, no wildcard.")
.address(options.bitcoind_network)
.expect("Always a P2WSH address");
.derive(index.into(), secp)
.address(options.bitcoind_network);
query += &format!(
"INSERT INTO addresses (address, derivation_index) VALUES (\"{}\", {});\n",
address, index

View File

@ -13,6 +13,8 @@ use miniscript::{
use std::{error, fmt, io::Write, str, sync};
use serde::{Deserialize, Serialize};
// Flag applied to the nSequence and CSV value before comparing them.
//
// <https://github.com/bitcoin/bitcoin/blob/4a540683ec40393d6369da1a9e02e45614db936d/src/primitives/transaction.h#L87-L89>
@ -24,6 +26,7 @@ pub enum DescCreationError {
InvalidKey(descriptor::DescriptorPublicKey),
Miniscript(miniscript::Error),
IncompatibleDesc,
DerivedKeyParsing,
}
impl std::fmt::Display for DescCreationError {
@ -35,6 +38,7 @@ impl std::fmt::Display for DescCreationError {
}
Self::Miniscript(e) => write!(f, "Miniscript error: '{}'.", e),
Self::IncompatibleDesc => write!(f, "Descriptor is not compatible."),
Self::DerivedKeyParsing => write!(f, "Parsing derived key,"),
}
}
}
@ -64,6 +68,54 @@ impl fmt::Display for DerivedPublicKey {
}
}
impl str::FromStr for DerivedPublicKey {
type Err = DescCreationError;
fn from_str(s: &str) -> Result<DerivedPublicKey, Self::Err> {
// The key is always of the form:
// [ fingerprint / index ]<key>
// 1 + 8 + 1 + 1 + 1 + 66 minimum
if s.len() < 78 {
return Err(DescCreationError::DerivedKeyParsing);
}
// Non-ASCII?
for ch in s.as_bytes() {
if *ch < 20 || *ch > 127 {
return Err(DescCreationError::DerivedKeyParsing);
}
}
if s.chars().next().expect("Size checked above") != '[' {
return Err(DescCreationError::DerivedKeyParsing);
}
let mut parts = s[1..].split(']');
let fg_deriv = parts.next().ok_or(DescCreationError::DerivedKeyParsing)?;
let key_str = parts.next().ok_or(DescCreationError::DerivedKeyParsing)?;
if fg_deriv.len() < 10 {
return Err(DescCreationError::DerivedKeyParsing);
}
let fingerprint = bip32::Fingerprint::from_str(&fg_deriv[..8])
.map_err(|_| DescCreationError::DerivedKeyParsing)?;
let deriv_index = bip32::ChildNumber::from_str(&fg_deriv[9..])
.map_err(|_| DescCreationError::DerivedKeyParsing)?;
if deriv_index.is_hardened() {
return Err(DescCreationError::DerivedKeyParsing);
}
let key = bitcoin::PublicKey::from_str(&key_str)
.map_err(|_| DescCreationError::DerivedKeyParsing)?;
Ok(DerivedPublicKey {
key,
origin: (fingerprint, deriv_index),
})
}
}
impl MiniscriptKey for DerivedPublicKey {
// This allows us to be able to derive keys and key source even for PkH s
type Hash = Self;
@ -117,11 +169,11 @@ fn is_unhardened_deriv(key: &descriptor::DescriptorPublicKey) -> bool {
/// A Miniscript descriptor with a main, unencombered, branch (the main owner of the coins)
/// and a timelocked branch (the heir).
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct InheritanceDescriptor(descriptor::Descriptor<descriptor::DescriptorPublicKey>);
/// Derived (containing only raw Bitcoin public keys) version of the inheritance descriptor.
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct DerivedInheritanceDescriptor(descriptor::Descriptor<DerivedPublicKey>);
impl fmt::Display for InheritanceDescriptor {
@ -210,7 +262,7 @@ impl InheritanceDescriptor {
owner_key: descriptor::DescriptorPublicKey,
heir_key: descriptor::DescriptorPublicKey,
timelock: u32,
) -> Result<descriptor::Descriptor<descriptor::DescriptorPublicKey>, DescCreationError> {
) -> Result<InheritanceDescriptor, DescCreationError> {
csv_check(timelock)?;
if let Some(key) = vec![&owner_key, &heir_key]
@ -247,9 +299,13 @@ impl InheritanceDescriptor {
miniscript::Segwitv0::check_local_validity(&tl_miniscript)
.expect("Miniscript must be sane");
Ok(descriptor::Descriptor::Wsh(
Ok(InheritanceDescriptor(descriptor::Descriptor::Wsh(
descriptor::Wsh::new(tl_miniscript).expect("Must pass sanity checks"),
))
)))
}
pub fn as_inner(&self) -> &descriptor::Descriptor<descriptor::DescriptorPublicKey> {
&self.0
}
/// Derive this descriptor at a given index.

View File

@ -377,10 +377,11 @@ mod tests {
use super::*;
use crate::{
config::{BitcoinConfig, BitcoindConfig},
descriptors::InheritanceDescriptor,
testutils::*,
};
use miniscript::{bitcoin, Descriptor, DescriptorPublicKey};
use miniscript::bitcoin;
use std::{
fs,
io::{BufRead, BufReader, Write},
@ -588,7 +589,7 @@ mod tests {
// Create a dummy config with this bitcoind
let desc_str = "wsh(andor(pk(xpub68JJTXc1MWK8KLW4HGLXZBJknja7kDUJuFHnM424LbziEXsfkh1WQCiEjjHw4zLqSUm4rvhgyGkkuRowE9tCJSgt3TQB5J3SKAbZ2SdcKST/*),older(10000),pk(xpub68JJTXc1MWK8PEQozKsRatrUHXKFNkD1Cb1BuQU9Xr5moCv87anqGyXLyUd4KpnDyZgo3gz4aN1r3NiaoweFW8UutBsBbgKHzaD5HkTkifK/*)))#tk6wzexy";
let desc = Descriptor::<DescriptorPublicKey>::from_str(desc_str).unwrap();
let desc = InheritanceDescriptor::from_str(desc_str).unwrap();
let config = Config {
bitcoin_config,
bitcoind_config: Some(bitcoind_config),

View File

@ -177,7 +177,8 @@ impl DummyMinisafe {
let owner_key = descriptor::DescriptorPublicKey::from_str("xpub68JJTXc1MWK8KLW4HGLXZBJknja7kDUJuFHnM424LbziEXsfkh1WQCiEjjHw4zLqSUm4rvhgyGkkuRowE9tCJSgt3TQB5J3SKAbZ2SdcKST/*").unwrap();
let heir_key = descriptor::DescriptorPublicKey::from_str("xpub68JJTXc1MWK8PEQozKsRatrUHXKFNkD1Cb1BuQU9Xr5moCv87anqGyXLyUd4KpnDyZgo3gz4aN1r3NiaoweFW8UutBsBbgKHzaD5HkTkifK/*").unwrap();
let desc = crate::descriptors::InheritanceDescriptor::new(owner_key, heir_key, 10_000).unwrap();
let desc =
crate::descriptors::InheritanceDescriptor::new(owner_key, heir_key, 10_000).unwrap();
let config = Config {
bitcoin_config,
bitcoind_config: None,

View File

@ -117,7 +117,7 @@ def minisafed(bitcoind, directory):
os.makedirs(datadir, exist_ok=True)
bitcoind_cookie = os.path.join(bitcoind.bitcoin_dir, "regtest", ".cookie")
main_desc = "wsh(or_d(pk(tpubD9vQiBdDxYzU1V5D5UUmMTXF9FZC13PuQDs4aiv6rF7UCKQFvtVKZguYakX12C2bt8736ksioxu9Y9Nmp18gj4jDeNJEEqrBPEZXAxe5YcQ/*),and_v(v:pkh(tpubD9vQiBdDxYzU4cVFtApWj4devZrvcfWaPXX1zHdDc7GPfUsDKqGnbhraccfm7BAXgRgUbVQUV2v2o4NitjGEk7hpbuP85kvBrD4ahFDtNBJ/*),older(157680))))"
main_desc = "wsh(or_d(pk(tpubD9vQiBdDxYzU1V5D5UUmMTXF9FZC13PuQDs4aiv6rF7UCKQFvtVKZguYakX12C2bt8736ksioxu9Y9Nmp18gj4jDeNJEEqrBPEZXAxe5YcQ/*),and_v(v:pkh(tpubD9vQiBdDxYzU4cVFtApWj4devZrvcfWaPXX1zHdDc7GPfUsDKqGnbhraccfm7BAXgRgUbVQUV2v2o4NitjGEk7hpbuP85kvBrD4ahFDtNBJ/*),older(65000))))"
minisafed = Minisafed(
datadir,