Skip to content

Commit 523654e

Browse files
committed
descriptor: add Descriptor::iter_pk
This commit was -not- AI-generated, though probably it should've been.
1 parent 6d1da92 commit 523654e

File tree

2 files changed

+152
-0
lines changed

2 files changed

+152
-0
lines changed

src/descriptor/iter.rs

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Iterators over descriptors
4+
5+
use crate::descriptor::{TapTreeIter, Tr};
6+
use crate::miniscript::context::{BareCtx, Legacy, Segwitv0, Tap};
7+
use crate::{miniscript, Miniscript, MiniscriptKey};
8+
9+
/// Iterator over all the keys in a descriptor.
10+
pub struct PkIter<'desc, Pk: MiniscriptKey> {
11+
single_key: Option<Pk>,
12+
taptree_iter: Option<TapTreeIter<'desc, Pk>>,
13+
ms_iter_bare: Option<miniscript::iter::PkIter<'desc, Pk, BareCtx>>,
14+
ms_iter_legacy: Option<miniscript::iter::PkIter<'desc, Pk, Legacy>>,
15+
ms_iter_segwit: Option<miniscript::iter::PkIter<'desc, Pk, Segwitv0>>,
16+
ms_iter_taproot: Option<miniscript::iter::PkIter<'desc, Pk, Tap>>,
17+
sorted_multi: Option<core::slice::Iter<'desc, Pk>>,
18+
}
19+
20+
impl<'desc, Pk: MiniscriptKey> PkIter<'desc, Pk> {
21+
pub(super) fn from_key(pk: Pk) -> Self {
22+
Self {
23+
single_key: Some(pk),
24+
taptree_iter: None,
25+
ms_iter_bare: None,
26+
ms_iter_legacy: None,
27+
ms_iter_segwit: None,
28+
ms_iter_taproot: None,
29+
sorted_multi: None,
30+
}
31+
}
32+
33+
pub(super) fn from_miniscript_bare(ms: &'desc Miniscript<Pk, BareCtx>) -> Self {
34+
Self {
35+
single_key: None,
36+
taptree_iter: None,
37+
ms_iter_bare: Some(ms.iter_pk()),
38+
ms_iter_legacy: None,
39+
ms_iter_segwit: None,
40+
ms_iter_taproot: None,
41+
sorted_multi: None,
42+
}
43+
}
44+
45+
pub(super) fn from_miniscript_legacy(ms: &'desc Miniscript<Pk, Legacy>) -> Self {
46+
Self {
47+
single_key: None,
48+
taptree_iter: None,
49+
ms_iter_bare: None,
50+
ms_iter_legacy: Some(ms.iter_pk()),
51+
ms_iter_segwit: None,
52+
ms_iter_taproot: None,
53+
sorted_multi: None,
54+
}
55+
}
56+
57+
pub(super) fn from_miniscript_segwit(ms: &'desc Miniscript<Pk, Segwitv0>) -> Self {
58+
Self {
59+
single_key: None,
60+
taptree_iter: None,
61+
ms_iter_bare: None,
62+
ms_iter_legacy: None,
63+
ms_iter_segwit: Some(ms.iter_pk()),
64+
ms_iter_taproot: None,
65+
sorted_multi: None,
66+
}
67+
}
68+
69+
pub(super) fn from_sortedmulti(sm: &'desc [Pk]) -> Self {
70+
Self {
71+
single_key: None,
72+
taptree_iter: None,
73+
ms_iter_bare: None,
74+
ms_iter_legacy: None,
75+
ms_iter_segwit: None,
76+
ms_iter_taproot: None,
77+
sorted_multi: Some(sm.iter()),
78+
}
79+
}
80+
81+
pub(super) fn from_tr(tr: &'desc Tr<Pk>) -> Self {
82+
Self {
83+
single_key: Some(tr.internal_key().clone()),
84+
taptree_iter: Some(tr.leaves()),
85+
ms_iter_bare: None,
86+
ms_iter_legacy: None,
87+
ms_iter_segwit: None,
88+
ms_iter_taproot: None,
89+
sorted_multi: None,
90+
}
91+
}
92+
}
93+
94+
impl<'desc, Pk: MiniscriptKey> Iterator for PkIter<'desc, Pk> {
95+
type Item = Pk;
96+
97+
#[rustfmt::skip] // the tower of .or_elses looks good as is
98+
fn next(&mut self) -> Option<Self::Item> {
99+
// If there is a single key, return it first. (This will be the case
100+
// for all single-key-only iterators but also for Taproot, where the
101+
// single key is the root key.)
102+
if let Some(k) = self.single_key.take() {
103+
return Some(k.clone());
104+
}
105+
106+
// Then attempt to yield something from the Taptree iterator.
107+
loop {
108+
if let Some(item) = self.ms_iter_taproot.as_mut().and_then(Iterator::next) {
109+
return Some(item);
110+
}
111+
if let Some(iter) = self.taptree_iter.as_mut().and_then(Iterator::next) {
112+
self.ms_iter_taproot = Some(iter.miniscript().iter_pk());
113+
} else {
114+
break;
115+
}
116+
}
117+
118+
// Finally run through the train of other iterators.
119+
self.ms_iter_bare.as_mut().and_then(Iterator::next).or_else(
120+
|| self.ms_iter_legacy.as_mut().and_then(Iterator::next).or_else(
121+
|| self.ms_iter_segwit.as_mut().and_then(Iterator::next).or_else(
122+
|| self.sorted_multi.as_mut().and_then(Iterator::next).cloned()
123+
)
124+
)
125+
)
126+
}
127+
}

src/descriptor/mod.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,15 @@ use crate::{
3232
};
3333

3434
mod bare;
35+
mod iter;
3536
mod segwitv0;
3637
mod sh;
3738
mod sortedmulti;
3839
mod tr;
3940

4041
// Descriptor Exports
4142
pub use self::bare::{Bare, Pkh};
43+
pub use self::iter::PkIter;
4244
pub use self::segwitv0::{Wpkh, Wsh, WshInner};
4345
pub use self::sh::{Sh, ShInner};
4446
pub use self::sortedmulti::SortedMultiVec;
@@ -241,6 +243,29 @@ impl<Pk: MiniscriptKey> Descriptor<Pk> {
241243
Ok(Descriptor::Tr(Tr::new(key, script)?))
242244
}
243245

246+
/// An iterator over all the keys referenced in the descriptor.
247+
pub fn iter_pk(&self) -> PkIter<'_, Pk> {
248+
match *self {
249+
Descriptor::Bare(ref bare) => PkIter::from_miniscript_bare(bare.as_inner()),
250+
Descriptor::Pkh(ref pk) => PkIter::from_key(pk.as_inner().clone()),
251+
Descriptor::Wpkh(ref pk) => PkIter::from_key(pk.as_inner().clone()),
252+
Descriptor::Sh(ref sh) => match *sh.as_inner() {
253+
ShInner::Wsh(ref wsh) => match wsh.as_inner() {
254+
WshInner::SortedMulti(ref sorted) => PkIter::from_sortedmulti(sorted.pks()),
255+
WshInner::Ms(ref ms) => PkIter::from_miniscript_segwit(ms),
256+
},
257+
ShInner::Wpkh(ref pk) => PkIter::from_key(pk.as_inner().clone()),
258+
ShInner::SortedMulti(ref sorted) => PkIter::from_sortedmulti(sorted.pks()),
259+
ShInner::Ms(ref ms) => PkIter::from_miniscript_legacy(ms),
260+
},
261+
Descriptor::Wsh(ref wsh) => match wsh.as_inner() {
262+
WshInner::SortedMulti(ref sorted) => PkIter::from_sortedmulti(sorted.pks()),
263+
WshInner::Ms(ref ms) => PkIter::from_miniscript_segwit(ms),
264+
},
265+
Descriptor::Tr(ref tr) => PkIter::from_tr(tr),
266+
}
267+
}
268+
244269
/// For a Taproot descriptor, returns the internal key.
245270
pub fn internal_key(&self) -> Option<&Pk> {
246271
if let Descriptor::Tr(ref tr) = self {

0 commit comments

Comments
 (0)