descriptors: cache the receive and change descriptors

This commit is contained in:
Antoine Poinsot 2022-10-24 09:39:32 +02:00
parent ca3d7c1f33
commit 4f3daa7741
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
3 changed files with 58 additions and 48 deletions

View File

@ -223,7 +223,10 @@ pub fn looper(
) {
let mut last_poll = None;
let mut synced = false;
let descs = [desc.receive_descriptor(), desc.change_descriptor()];
let descs = [
desc.receive_descriptor().clone(),
desc.change_descriptor().clone(),
];
maybe_initialize_tip(&bit, &db);

View File

@ -194,7 +194,11 @@ fn is_valid_desc_key(key: &descriptor::DescriptorPublicKey) -> bool {
/// An [InheritanceDescriptor] that contains multipath keys for (and only for) the receive keychain
/// and the change keychain.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MultipathDescriptor(descriptor::Descriptor<descriptor::DescriptorPublicKey>);
pub struct MultipathDescriptor {
multi_desc: descriptor::Descriptor<descriptor::DescriptorPublicKey>,
receive_desc: InheritanceDescriptor,
change_desc: InheritanceDescriptor,
}
/// A Miniscript descriptor with a main, unencombered, branch (the main owner of the coins)
/// and a timelocked branch (the heir). All keys in this descriptor are singlepath.
@ -207,7 +211,7 @@ pub struct DerivedInheritanceDescriptor(descriptor::Descriptor<DerivedPublicKey>
impl fmt::Display for MultipathDescriptor {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
write!(f, "{}", self.multi_desc)
}
}
@ -277,8 +281,26 @@ impl str::FromStr for MultipathDescriptor {
.iter()
.find(|s| matches!(s, SemanticPolicy::Key(_)))
.ok_or(DescCreationError::IncompatibleDesc)?;
let multi_desc = descriptor::Descriptor::Wsh(wsh_desc);
Ok(MultipathDescriptor(descriptor::Descriptor::Wsh(wsh_desc)))
// Compute the receive and change "sub" descriptors right away. According to our pubkey
// check above, there must be only two of those, 0 and 1.
// We use /0/* for receiving and /1/* for change.
// FIXME: don't rely on into_single_descs()'s ordering.
let mut singlepath_descs = multi_desc
.clone()
.into_single_descriptors()
.expect("Can't error, all paths have the same length")
.into_iter();
assert_eq!(singlepath_descs.len(), 2);
let receive_desc = InheritanceDescriptor(singlepath_descs.next().expect("First of 2"));
let change_desc = InheritanceDescriptor(singlepath_descs.next().expect("Second of 2"));
Ok(MultipathDescriptor {
multi_desc,
receive_desc,
change_desc,
})
}
}
@ -341,15 +363,33 @@ impl MultipathDescriptor {
.expect("Well typed");
miniscript::Segwitv0::check_local_validity(&tl_miniscript)
.expect("Miniscript must be sane");
Ok(MultipathDescriptor(descriptor::Descriptor::Wsh(
let multi_desc = descriptor::Descriptor::Wsh(
descriptor::Wsh::new(tl_miniscript).expect("Must pass sanity checks"),
)))
);
// Compute the receive and change "sub" descriptors right away. According to our pubkey
// check above, there must be only two of those, 0 and 1.
// We use /0/* for receiving and /1/* for change.
// FIXME: don't rely on into_single_descs()'s ordering.
let mut singlepath_descs = multi_desc
.clone()
.into_single_descriptors()
.expect("Can't error, all paths have the same length")
.into_iter();
assert_eq!(singlepath_descs.len(), 2);
let receive_desc = InheritanceDescriptor(singlepath_descs.next().expect("First of 2"));
let change_desc = InheritanceDescriptor(singlepath_descs.next().expect("Second of 2"));
Ok(MultipathDescriptor {
multi_desc,
receive_desc,
change_desc,
})
}
/// Whether all xpubs contained in this descriptor are for the passed expected network.
pub fn all_xpubs_net_is(&self, expected_net: bitcoin::Network) -> bool {
self.0.for_each_key(|xpub| {
self.multi_desc.for_each_key(|xpub| {
if let descriptor::DescriptorPublicKey::MultiXPub(xpub) = xpub {
xpub.xkey.network == expected_net
} else {
@ -358,52 +398,19 @@ impl MultipathDescriptor {
})
}
// TODO: Cache it inside the struct, it's very inefficient to use into_single_descriptors() for
// every single derivation.
/// Get the descriptor for receiving addresses.
pub fn receive_descriptor(&self) -> InheritanceDescriptor {
let singlepath_descs = self
.0
.clone()
.into_single_descriptors()
.expect("Can't error, all paths have the same length");
assert_eq!(singlepath_descs.len(), 2);
// We use /0/* for receiving, so it's the first descriptor between <0;1>.
// FIXME: don't rely on ordering.
InheritanceDescriptor(
singlepath_descs
.into_iter()
.next()
.expect("Just checked the length"),
)
pub fn receive_descriptor(&self) -> &InheritanceDescriptor {
&self.receive_desc
}
// TODO: Cache it inside the struct, it's very inefficient to use into_single_descriptors() for
// every single derivation.
/// Get the descriptor for change addresses.
pub fn change_descriptor(&self) -> InheritanceDescriptor {
let singlepath_descs = self
.0
.clone()
.into_single_descriptors()
.expect("Can't error, all paths have the same length");
assert_eq!(singlepath_descs.len(), 2);
// We use /1/* for change, so it's the second descriptor between <0;1>.
// FIXME: don't rely on ordering.
InheritanceDescriptor(
singlepath_descs
.into_iter()
.rev()
.next()
.expect("Just checked the length"),
)
pub fn change_descriptor(&self) -> &InheritanceDescriptor {
&self.change_desc
}
/// Get the value (in blocks) of the relative timelock for the heir's spending path.
pub fn timelock_value(&self) -> u32 {
let wsh_desc = match &self.0 {
let wsh_desc = match &self.multi_desc {
descriptor::Descriptor::Wsh(desc) => desc,
_ => unreachable!(),
};

View File

@ -600,8 +600,8 @@ mod tests {
// Create a dummy config with this bitcoind
let desc_str = "wsh(andor(pk(xpub68JJTXc1MWK8KLW4HGLXZBJknja7kDUJuFHnM424LbziEXsfkh1WQCiEjjHw4zLqSUm4rvhgyGkkuRowE9tCJSgt3TQB5J3SKAbZ2SdcKST/<0;1>/*),older(10000),pk(xpub68JJTXc1MWK8PEQozKsRatrUHXKFNkD1Cb1BuQU9Xr5moCv87anqGyXLyUd4KpnDyZgo3gz4aN1r3NiaoweFW8UutBsBbgKHzaD5HkTkifK/<0;1>/*)))#yudtr0k5";
let desc = MultipathDescriptor::from_str(desc_str).unwrap();
let receive_desc = desc.receive_descriptor();
let change_desc = desc.change_descriptor();
let receive_desc = desc.receive_descriptor().clone();
let change_desc = desc.change_descriptor().clone();
let config = Config {
bitcoin_config,
bitcoind_config: Some(bitcoind_config),