Skip to content

Commit 89d798f

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 89d798f

File tree

9 files changed

+92
-146
lines changed

9 files changed

+92
-146
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);

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

src/descriptor/segwitv0.rs

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

3232
use super::{
3333
checksum::{desc_checksum, verify_checksum},
34-
DescriptorTrait, SortedMultiVec,
34+
DescriptorTrait, SortedMultiVec, ToAddress,
3535
};
3636
/// A Segwitv0 wsh descriptor
3737
#[derive(Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
@@ -79,15 +79,6 @@ impl<Pk: MiniscriptKey> Wsh<Pk> {
7979
}
8080

8181
impl<Pk: MiniscriptKey + ToPublicKey> Wsh<Pk> {
82-
/// Obtains the corresponding script pubkey for this descriptor.
83-
/// Called by [`DescriptorTrait::address`] for this descriptor.
84-
pub fn addr(&self, network: Network) -> Address {
85-
match self.inner {
86-
WshInner::SortedMulti(ref smv) => Address::p2wsh(&smv.encode(), network),
87-
WshInner::Ms(ref ms) => Address::p2wsh(&ms.encode(), network),
88-
}
89-
}
90-
9182
/// Obtains the underlying miniscript for this descriptor.
9283
/// Called by [`DescriptorTrait::explicit_script`] for this descriptor.
9384
pub fn inner_script(&self) -> Script {
@@ -194,13 +185,6 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Wsh<Pk> {
194185
Ok(())
195186
}
196187

197-
fn address(&self, network: Network) -> Result<Address, Error>
198-
where
199-
Pk: ToPublicKey,
200-
{
201-
Ok(self.addr(network))
202-
}
203-
204188
fn script_pubkey(&self) -> Script
205189
where
206190
Pk: ToPublicKey,
@@ -279,6 +263,15 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Wsh<Pk> {
279263
}
280264
}
281265

266+
impl<Pk: MiniscriptKey + ToPublicKey> ToAddress<Pk> for Wsh<Pk> {
267+
fn to_address(&self, network: Network) -> Address {
268+
match self.inner {
269+
WshInner::SortedMulti(ref smv) => Address::p2wsh(&smv.encode(), network),
270+
WshInner::Ms(ref ms) => Address::p2wsh(&ms.encode(), network),
271+
}
272+
}
273+
}
274+
282275
impl<Pk: MiniscriptKey> ForEachKey<Pk> for Wsh<Pk> {
283276
fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, pred: F) -> bool
284277
where
@@ -354,12 +347,6 @@ impl<Pk: MiniscriptKey> Wpkh<Pk> {
354347
}
355348

356349
impl<Pk: MiniscriptKey + ToPublicKey> Wpkh<Pk> {
357-
/// Obtains the [`Address`] for this descriptor.
358-
pub fn addr(&self, network: Network) -> Address {
359-
Address::p2wpkh(&self.pk.to_public_key(), network)
360-
.expect("Rust Miniscript types don't allow uncompressed pks in segwit descriptors")
361-
}
362-
363350
/// Obtains the underlying miniscript for this descriptor.
364351
/// Called by [`DescriptorTrait::explicit_script`] for this descriptor.
365352
/// Equivalent to [`DescriptorTrait::script_pubkey`] for this descriptor.
@@ -448,13 +435,6 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Wpkh<Pk> {
448435
}
449436
}
450437

451-
fn address(&self, network: Network) -> Result<Address, Error>
452-
where
453-
Pk: ToPublicKey,
454-
{
455-
Ok(self.addr(network))
456-
}
457-
458438
fn script_pubkey(&self) -> Script
459439
where
460440
Pk: ToPublicKey,
@@ -514,6 +494,16 @@ impl<Pk: MiniscriptKey> DescriptorTrait<Pk> for Wpkh<Pk> {
514494
}
515495
}
516496

497+
impl<Pk: MiniscriptKey + ToPublicKey> ToAddress<Pk> for Wpkh<Pk> {
498+
fn to_address(&self, network: Network) -> Address {
499+
// We check this in the constructor.
500+
assert!(!self.pk.is_uncompressed());
501+
502+
Address::p2wpkh(&self.pk.to_public_key(), network)
503+
.expect("infallible for compressed pubkey")
504+
}
505+
}
506+
517507
impl<Pk: MiniscriptKey> ForEachKey<Pk> for Wpkh<Pk> {
518508
fn for_each_key<'a, F: FnMut(ForEach<'a, Pk>) -> bool>(&'a self, mut pred: F) -> bool
519509
where

0 commit comments

Comments
 (0)