Skip to content

Commit 4f3daa7

Browse files
committed
descriptors: cache the receive and change descriptors
1 parent ca3d7c1 commit 4f3daa7

3 files changed

Lines changed: 58 additions & 48 deletions

File tree

src/bitcoin/poller/looper.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,10 @@ pub fn looper(
223223
) {
224224
let mut last_poll = None;
225225
let mut synced = false;
226-
let descs = [desc.receive_descriptor(), desc.change_descriptor()];
226+
let descs = [
227+
desc.receive_descriptor().clone(),
228+
desc.change_descriptor().clone(),
229+
];
227230

228231
maybe_initialize_tip(&bit, &db);
229232

src/descriptors.rs

Lines changed: 52 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,11 @@ fn is_valid_desc_key(key: &descriptor::DescriptorPublicKey) -> bool {
194194
/// An [InheritanceDescriptor] that contains multipath keys for (and only for) the receive keychain
195195
/// and the change keychain.
196196
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
197-
pub struct MultipathDescriptor(descriptor::Descriptor<descriptor::DescriptorPublicKey>);
197+
pub struct MultipathDescriptor {
198+
multi_desc: descriptor::Descriptor<descriptor::DescriptorPublicKey>,
199+
receive_desc: InheritanceDescriptor,
200+
change_desc: InheritanceDescriptor,
201+
}
198202

199203
/// A Miniscript descriptor with a main, unencombered, branch (the main owner of the coins)
200204
/// and a timelocked branch (the heir). All keys in this descriptor are singlepath.
@@ -207,7 +211,7 @@ pub struct DerivedInheritanceDescriptor(descriptor::Descriptor<DerivedPublicKey>
207211

208212
impl fmt::Display for MultipathDescriptor {
209213
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210-
write!(f, "{}", self.0)
214+
write!(f, "{}", self.multi_desc)
211215
}
212216
}
213217

@@ -277,8 +281,26 @@ impl str::FromStr for MultipathDescriptor {
277281
.iter()
278282
.find(|s| matches!(s, SemanticPolicy::Key(_)))
279283
.ok_or(DescCreationError::IncompatibleDesc)?;
284+
let multi_desc = descriptor::Descriptor::Wsh(wsh_desc);
280285

281-
Ok(MultipathDescriptor(descriptor::Descriptor::Wsh(wsh_desc)))
286+
// Compute the receive and change "sub" descriptors right away. According to our pubkey
287+
// check above, there must be only two of those, 0 and 1.
288+
// We use /0/* for receiving and /1/* for change.
289+
// FIXME: don't rely on into_single_descs()'s ordering.
290+
let mut singlepath_descs = multi_desc
291+
.clone()
292+
.into_single_descriptors()
293+
.expect("Can't error, all paths have the same length")
294+
.into_iter();
295+
assert_eq!(singlepath_descs.len(), 2);
296+
let receive_desc = InheritanceDescriptor(singlepath_descs.next().expect("First of 2"));
297+
let change_desc = InheritanceDescriptor(singlepath_descs.next().expect("Second of 2"));
298+
299+
Ok(MultipathDescriptor {
300+
multi_desc,
301+
receive_desc,
302+
change_desc,
303+
})
282304
}
283305
}
284306

@@ -341,15 +363,33 @@ impl MultipathDescriptor {
341363
.expect("Well typed");
342364
miniscript::Segwitv0::check_local_validity(&tl_miniscript)
343365
.expect("Miniscript must be sane");
344-
345-
Ok(MultipathDescriptor(descriptor::Descriptor::Wsh(
366+
let multi_desc = descriptor::Descriptor::Wsh(
346367
descriptor::Wsh::new(tl_miniscript).expect("Must pass sanity checks"),
347-
)))
368+
);
369+
370+
// Compute the receive and change "sub" descriptors right away. According to our pubkey
371+
// check above, there must be only two of those, 0 and 1.
372+
// We use /0/* for receiving and /1/* for change.
373+
// FIXME: don't rely on into_single_descs()'s ordering.
374+
let mut singlepath_descs = multi_desc
375+
.clone()
376+
.into_single_descriptors()
377+
.expect("Can't error, all paths have the same length")
378+
.into_iter();
379+
assert_eq!(singlepath_descs.len(), 2);
380+
let receive_desc = InheritanceDescriptor(singlepath_descs.next().expect("First of 2"));
381+
let change_desc = InheritanceDescriptor(singlepath_descs.next().expect("Second of 2"));
382+
383+
Ok(MultipathDescriptor {
384+
multi_desc,
385+
receive_desc,
386+
change_desc,
387+
})
348388
}
349389

350390
/// Whether all xpubs contained in this descriptor are for the passed expected network.
351391
pub fn all_xpubs_net_is(&self, expected_net: bitcoin::Network) -> bool {
352-
self.0.for_each_key(|xpub| {
392+
self.multi_desc.for_each_key(|xpub| {
353393
if let descriptor::DescriptorPublicKey::MultiXPub(xpub) = xpub {
354394
xpub.xkey.network == expected_net
355395
} else {
@@ -358,52 +398,19 @@ impl MultipathDescriptor {
358398
})
359399
}
360400

361-
// TODO: Cache it inside the struct, it's very inefficient to use into_single_descriptors() for
362-
// every single derivation.
363401
/// Get the descriptor for receiving addresses.
364-
pub fn receive_descriptor(&self) -> InheritanceDescriptor {
365-
let singlepath_descs = self
366-
.0
367-
.clone()
368-
.into_single_descriptors()
369-
.expect("Can't error, all paths have the same length");
370-
assert_eq!(singlepath_descs.len(), 2);
371-
372-
// We use /0/* for receiving, so it's the first descriptor between <0;1>.
373-
// FIXME: don't rely on ordering.
374-
InheritanceDescriptor(
375-
singlepath_descs
376-
.into_iter()
377-
.next()
378-
.expect("Just checked the length"),
379-
)
402+
pub fn receive_descriptor(&self) -> &InheritanceDescriptor {
403+
&self.receive_desc
380404
}
381405

382-
// TODO: Cache it inside the struct, it's very inefficient to use into_single_descriptors() for
383-
// every single derivation.
384406
/// Get the descriptor for change addresses.
385-
pub fn change_descriptor(&self) -> InheritanceDescriptor {
386-
let singlepath_descs = self
387-
.0
388-
.clone()
389-
.into_single_descriptors()
390-
.expect("Can't error, all paths have the same length");
391-
assert_eq!(singlepath_descs.len(), 2);
392-
393-
// We use /1/* for change, so it's the second descriptor between <0;1>.
394-
// FIXME: don't rely on ordering.
395-
InheritanceDescriptor(
396-
singlepath_descs
397-
.into_iter()
398-
.rev()
399-
.next()
400-
.expect("Just checked the length"),
401-
)
407+
pub fn change_descriptor(&self) -> &InheritanceDescriptor {
408+
&self.change_desc
402409
}
403410

404411
/// Get the value (in blocks) of the relative timelock for the heir's spending path.
405412
pub fn timelock_value(&self) -> u32 {
406-
let wsh_desc = match &self.0 {
413+
let wsh_desc = match &self.multi_desc {
407414
descriptor::Descriptor::Wsh(desc) => desc,
408415
_ => unreachable!(),
409416
};

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -600,8 +600,8 @@ mod tests {
600600
// Create a dummy config with this bitcoind
601601
let desc_str = "wsh(andor(pk(xpub68JJTXc1MWK8KLW4HGLXZBJknja7kDUJuFHnM424LbziEXsfkh1WQCiEjjHw4zLqSUm4rvhgyGkkuRowE9tCJSgt3TQB5J3SKAbZ2SdcKST/<0;1>/*),older(10000),pk(xpub68JJTXc1MWK8PEQozKsRatrUHXKFNkD1Cb1BuQU9Xr5moCv87anqGyXLyUd4KpnDyZgo3gz4aN1r3NiaoweFW8UutBsBbgKHzaD5HkTkifK/<0;1>/*)))#yudtr0k5";
602602
let desc = MultipathDescriptor::from_str(desc_str).unwrap();
603-
let receive_desc = desc.receive_descriptor();
604-
let change_desc = desc.change_descriptor();
603+
let receive_desc = desc.receive_descriptor().clone();
604+
let change_desc = desc.change_descriptor().clone();
605605
let config = Config {
606606
bitcoin_config,
607607
bitcoind_config: Some(bitcoind_config),

0 commit comments

Comments
 (0)