Skip to content

Commit 9cfc85e

Browse files
committed
Extract the address method out of DescriptorTrait
The code around the `address` method is a little convoluted because an address cannot be computed from a `Bare` descriptor. Extract the `address` method out of the `DescriptorTrait` as a new `ToAddress` trait that is infallible. Implement it for all the descriptor types that can be converted to an address, all of which can be done infallible. Some of these infallible conversions rely on an invariant upheld by the miniscript library.
1 parent a56fe85 commit 9cfc85e

File tree

11 files changed

+95
-149
lines changed

11 files changed

+95
-149
lines changed

examples/htlc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ extern crate miniscript;
2020
use bitcoin::Network;
2121
use miniscript::descriptor::Wsh;
2222
use miniscript::policy::{Concrete, Liftable};
23-
use miniscript::DescriptorTrait;
23+
use miniscript::{DescriptorTrait, ToAddress};
2424
use std::str::FromStr;
2525

2626
fn main() {
@@ -65,7 +65,7 @@ fn main() {
6565
);
6666

6767
assert_eq!(
68-
format!("{}", htlc_descriptor.address(Network::Bitcoin).unwrap()),
68+
format!("{}", htlc_descriptor.to_address(Network::Bitcoin)),
6969
"bc1qmpfcw7he9z5d9ftfe8qw699azmm2sr8fen903fs4plv007yx0t3qxfmqv5"
7070
);
7171
}

examples/xpub_descriptors.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
//! Example: Parsing a xpub and getting address
1616
1717
use miniscript::bitcoin::{self, secp256k1};
18-
use miniscript::{Descriptor, DescriptorPublicKey, DescriptorTrait, TranslatePk2};
18+
use miniscript::{Descriptor, DescriptorPublicKey, TranslatePk2};
1919

2020
use std::str::FromStr;
2121
fn main() {
@@ -28,15 +28,15 @@ fn main() {
2828
.unwrap()
2929
.translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx))
3030
.unwrap()
31-
.address(bitcoin::Network::Bitcoin).unwrap();
31+
.to_address(bitcoin::Network::Bitcoin).unwrap();
3232

3333
let addr_two = Descriptor::<DescriptorPublicKey>::from_str(
3434
"wsh(sortedmulti(1,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB))",
3535
)
3636
.unwrap()
3737
.translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx))
3838
.unwrap()
39-
.address(bitcoin::Network::Bitcoin).unwrap();
39+
.to_address(bitcoin::Network::Bitcoin).unwrap();
4040
let expected = bitcoin::Address::from_str(
4141
"bc1qpq2cfgz5lktxzr5zqv7nrzz46hsvq3492ump9pz8rzcl8wqtwqcspx5y6a",
4242
)
@@ -51,15 +51,15 @@ fn main() {
5151
.unwrap()
5252
.derived_descriptor(&secp_ctx, 5)
5353
.unwrap()
54-
.address(bitcoin::Network::Bitcoin).unwrap();
54+
.to_address(bitcoin::Network::Bitcoin).unwrap();
5555

5656
let addr_two = Descriptor::<DescriptorPublicKey>::from_str(
5757
"sh(wsh(sortedmulti(1,xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH/0/0/*,xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB/1/0/*)))",
5858
)
5959
.unwrap()
6060
.derived_descriptor(&secp_ctx, 5)
6161
.unwrap()
62-
.address(bitcoin::Network::Bitcoin).unwrap();
62+
.to_address(bitcoin::Network::Bitcoin).unwrap();
6363
let expected = bitcoin::Address::from_str("325zcVBN5o2eqqqtGwPjmtDd8dJRyYP82s").unwrap();
6464
assert_eq!(addr_one, expected);
6565
assert_eq!(addr_two, expected);

integration_test/src/test_cpp.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ pub fn test_from_cpp_ms(cl: &Client, testdata: &TestData) {
8888
for wsh in desc_vec.iter() {
8989
let txid = cl
9090
.send_to_address(
91-
&wsh.address(bitcoin::Network::Regtest).unwrap(),
91+
&wsh.to_address(bitcoin::Network::Regtest).unwrap(),
9292
btc(1),
9393
None,
9494
None,

integration_test/src/test_desc.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use bitcoin::util::{psbt, sighash};
1313
use bitcoin::{self, Amount, OutPoint, SchnorrSig, Script, Transaction, TxIn, TxOut, Txid};
1414
use bitcoincore_rpc::{json, Client, RpcApi};
1515
use miniscript::miniscript::iter;
16-
use miniscript::psbt::{PsbtInputExt, PsbtExt};
16+
use miniscript::psbt::{PsbtExt, PsbtInputExt};
1717
use miniscript::{Descriptor, DescriptorTrait, Miniscript, ToPublicKey};
1818
use miniscript::{MiniscriptKey, ScriptContext};
1919
use std::collections::BTreeMap;
@@ -57,7 +57,7 @@ pub fn test_desc_satisfy(cl: &Client, testdata: &TestData, desc: &str) -> Witnes
5757
// Next send some btc to each address corresponding to the miniscript
5858
let txid = cl
5959
.send_to_address(
60-
&derived_desc.address(bitcoin::Network::Regtest).unwrap(),
60+
&derived_desc.to_address(bitcoin::Network::Regtest).unwrap(),
6161
btc(1),
6262
None,
6363
None,

src/descriptor/bare.rs

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use crate::{
3333

3434
use super::{
3535
checksum::{desc_checksum, verify_checksum},
36-
DescriptorTrait,
36+
DescriptorTrait, ToAddress,
3737
};
3838

3939
/// Create a Bare Descriptor. That is descriptor that is
@@ -135,13 +135,6 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Bare<Pk> {
135135
Ok(())
136136
}
137137

138-
fn address(&self, _network: Network) -> Result<Address, Error>
139-
where
140-
Pk: ToPublicKey,
141-
{
142-
Err(Error::BareDescriptorAddr)
143-
}
144-
145138
fn script_pubkey(&self) -> Script
146139
where
147140
Pk: ToPublicKey,
@@ -255,11 +248,6 @@ impl<Pk: MiniscriptKey> Pkh<Pk> {
255248
}
256249

257250
impl<Pk: MiniscriptKey + ToPublicKey> Pkh<Pk> {
258-
/// Obtains the [`Address`] for this descriptor.
259-
pub fn addr(&self, network: Network) -> Address {
260-
Address::p2pkh(&self.pk.to_public_key(), network)
261-
}
262-
263251
/// Obtains the underlying miniscript for this descriptor.
264252
/// Called by [`DescriptorTrait::explicit_script`] for this descriptor.
265253
/// Equivalent to [`DescriptorTrait::script_pubkey`] for this descriptor.
@@ -338,13 +326,6 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Pkh<Pk> {
338326
Ok(())
339327
}
340328

341-
fn address(&self, network: Network) -> Result<Address, Error>
342-
where
343-
Pk: ToPublicKey,
344-
{
345-
Ok(self.addr(network))
346-
}
347-
348329
fn script_pubkey(&self) -> Script
349330
where
350331
Pk: ToPublicKey,
@@ -406,6 +387,12 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Pkh<Pk> {
406387
}
407388
}
408389

390+
impl<Pk: MiniscriptKey + ToPublicKey> ToAddress<Pk> for Pkh<Pk> {
391+
fn to_address(&self, network: Network) -> Address {
392+
Address::p2pkh(&self.pk.to_public_key(), network)
393+
}
394+
}
395+
409396
impl<Pk: MiniscriptKey> ForEachKey<Pk> for Pkh<Pk> {
410397
fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool
411398
where

src/descriptor/mod.rs

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -92,14 +92,6 @@ pub trait DescriptorTrait<Pk: MiniscriptKey> {
9292
/// The signer may not be able to find satisfactions even if one exists
9393
fn sanity_check(&self) -> Result<(), Error>;
9494

95-
/// Computes the Bitcoin address of the descriptor, if one exists
96-
/// Some descriptors like pk() don't have any address.
97-
/// Errors:
98-
/// - On raw/bare descriptors that don't have any address
99-
fn address(&self, network: Network) -> Result<Address, Error>
100-
where
101-
Pk: ToPublicKey;
102-
10395
/// Computes the scriptpubkey of the descriptor
10496
fn script_pubkey(&self) -> Script
10597
where
@@ -175,6 +167,12 @@ pub trait DescriptorTrait<Pk: MiniscriptKey> {
175167
Pk: ToPublicKey;
176168
}
177169

170+
/// A trait for getting the address for a descriptor.
171+
pub trait ToAddress<Pk: MiniscriptKey + ToPublicKey> {
172+
/// Computes the Bitcoin address of the descriptor.
173+
fn to_address(&self, network: Network) -> Address;
174+
}
175+
178176
/// Script descriptor
179177
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
180178
pub enum Descriptor<Pk: MiniscriptKey> {
@@ -192,6 +190,20 @@ pub enum Descriptor<Pk: MiniscriptKey> {
192190
Tr(Tr<Pk>),
193191
}
194192

193+
impl<Pk: MiniscriptKey + ToPublicKey> Descriptor<Pk> {
194+
/// Computes the address for this descriptor.
195+
pub fn to_address(&self, network: Network) -> Result<Address, Error> {
196+
match *self {
197+
Descriptor::Bare(_) => Err(Error::BareDescriptorAddr),
198+
Descriptor::Pkh(ref pkh) => Ok(pkh.to_address(network)),
199+
Descriptor::Wpkh(ref wpkh) => Ok(wpkh.to_address(network)),
200+
Descriptor::Wsh(ref wsh) => Ok(wsh.to_address(network)),
201+
Descriptor::Sh(ref sh) => Ok(sh.to_address(network)),
202+
Descriptor::Tr(ref tr) => Ok(tr.to_address(network)),
203+
}
204+
}
205+
}
206+
195207
impl<Pk: MiniscriptKey> From<Bare<Pk>> for Descriptor<Pk> {
196208
#[inline]
197209
fn from(inner: Bare<Pk>) -> Self {
@@ -499,20 +511,6 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Descriptor<Pk> {
499511
Descriptor::Tr(ref tr) => tr.sanity_check(),
500512
}
501513
}
502-
/// Computes the Bitcoin address of the descriptor, if one exists
503-
fn address(&self, network: Network) -> Result<Address, Error>
504-
where
505-
Pk: ToPublicKey,
506-
{
507-
match *self {
508-
Descriptor::Bare(ref bare) => bare.address(network),
509-
Descriptor::Pkh(ref pkh) => pkh.address(network),
510-
Descriptor::Wpkh(ref wpkh) => wpkh.address(network),
511-
Descriptor::Wsh(ref wsh) => wsh.address(network),
512-
Descriptor::Sh(ref sh) => sh.address(network),
513-
Descriptor::Tr(ref tr) => tr.address(network),
514-
}
515-
}
516514

517515
/// Computes the scriptpubkey of the descriptor
518516
fn script_pubkey(&self) -> Script
@@ -984,7 +982,7 @@ mod tests {
984982
)
985983
);
986984
assert_eq!(
987-
bare.address(Network::Bitcoin).unwrap_err().to_string(),
985+
bare.to_address(Network::Bitcoin).unwrap_err().to_string(),
988986
"Bare descriptors don't have address"
989987
);
990988

@@ -1018,7 +1016,7 @@ mod tests {
10181016
.into_script()
10191017
);
10201018
assert_eq!(
1021-
pkh.address(Network::Bitcoin,).unwrap().to_string(),
1019+
pkh.to_address(Network::Bitcoin,).unwrap().to_string(),
10221020
"1D7nRvrRgzCg9kYBwhPH3j3Gs6SmsRg3Wq"
10231021
);
10241022

@@ -1039,7 +1037,7 @@ mod tests {
10391037
.into_script()
10401038
);
10411039
assert_eq!(
1042-
wpkh.address(Network::Bitcoin,).unwrap().to_string(),
1040+
wpkh.to_address(Network::Bitcoin,).unwrap().to_string(),
10431041
"bc1qsn57m9drscflq5nl76z6ny52hck5w4x5wqd9yt"
10441042
);
10451043

@@ -1061,7 +1059,7 @@ mod tests {
10611059
.into_script()
10621060
);
10631061
assert_eq!(
1064-
shwpkh.address(Network::Bitcoin,).unwrap().to_string(),
1062+
shwpkh.to_address(Network::Bitcoin,).unwrap().to_string(),
10651063
"3PjMEzoveVbvajcnDDuxcJhsuqPHgydQXq"
10661064
);
10671065

@@ -1083,7 +1081,7 @@ mod tests {
10831081
.into_script()
10841082
);
10851083
assert_eq!(
1086-
sh.address(Network::Bitcoin,).unwrap().to_string(),
1084+
sh.to_address(Network::Bitcoin,).unwrap().to_string(),
10871085
"3HDbdvM9CQ6ASnQFUkWw6Z4t3qNwMesJE9"
10881086
);
10891087

@@ -1109,7 +1107,7 @@ mod tests {
11091107
.into_script()
11101108
);
11111109
assert_eq!(
1112-
wsh.address(Network::Bitcoin,).unwrap().to_string(),
1110+
wsh.to_address(Network::Bitcoin,).unwrap().to_string(),
11131111
"bc1qlymeahyfsv2jm3upw3urqp6m65ufde9seedl7umh0lth6yjt5zzsk33tv6"
11141112
);
11151113

@@ -1131,7 +1129,7 @@ mod tests {
11311129
.into_script()
11321130
);
11331131
assert_eq!(
1134-
shwsh.address(Network::Bitcoin,).unwrap().to_string(),
1132+
shwsh.to_address(Network::Bitcoin,).unwrap().to_string(),
11351133
"38cTksiyPT2b1uGRVbVqHdDhW9vKs84N6Z"
11361134
);
11371135
}
@@ -1633,12 +1631,12 @@ mod tests {
16331631
let addr_one = desc_one
16341632
.translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx))
16351633
.unwrap()
1636-
.address(bitcoin::Network::Bitcoin)
1634+
.to_address(bitcoin::Network::Bitcoin)
16371635
.unwrap();
16381636
let addr_two = desc_two
16391637
.translate_pk2(|xpk| xpk.derive_public_key(&secp_ctx))
16401638
.unwrap()
1641-
.address(bitcoin::Network::Bitcoin)
1639+
.to_address(bitcoin::Network::Bitcoin)
16421640
.unwrap();
16431641
let addr_expected = bitcoin::Address::from_str(raw_addr_expected).unwrap();
16441642
assert_eq!(addr_one, addr_expected);

src/descriptor/pretaproot.rs

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,6 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for PreTaprootDescriptor<Pk> {
4141
PreTaprootDescriptor::Sh(ref sh) => sh.sanity_check(),
4242
}
4343
}
44-
/// Computes the Bitcoin address of the descriptor, if one exists
45-
fn address(&self, network: bitcoin::Network) -> Result<bitcoin::Address, Error>
46-
where
47-
Pk: ToPublicKey,
48-
{
49-
match *self {
50-
PreTaprootDescriptor::Bare(ref bare) => bare.address(network),
51-
PreTaprootDescriptor::Pkh(ref pkh) => pkh.address(network),
52-
PreTaprootDescriptor::Wpkh(ref wpkh) => wpkh.address(network),
53-
PreTaprootDescriptor::Wsh(ref wsh) => wsh.address(network),
54-
PreTaprootDescriptor::Sh(ref sh) => sh.address(network),
55-
}
56-
}
5744

5845
/// Computes the scriptpubkey of the descriptor
5946
fn script_pubkey(&self) -> Script

0 commit comments

Comments
 (0)