descriptor: Use DescriptorError instead of Error when reasonable

Change the return type of the `descriptor!()` macro and `ToWalletDescriptor` to
avoid having to map errors.

Also introduce more checks to validate descriptors built using the macro.
This commit is contained in:
Alekos Filini
2021-01-11 13:12:01 +01:00
parent 8510b2b86e
commit bf04a2cf69
9 changed files with 190 additions and 106 deletions

View File

@@ -49,7 +49,7 @@ pub mod policy;
pub mod template;
pub use self::checksum::get_checksum;
use self::error::Error;
pub use self::error::Error as DescriptorError;
pub use self::policy::Policy;
use self::template::DescriptorTemplateOut;
use crate::keys::{KeyError, ToDescriptorKey};
@@ -72,14 +72,14 @@ pub trait ToWalletDescriptor {
fn to_wallet_descriptor(
self,
network: Network,
) -> Result<(ExtendedDescriptor, KeyMap), KeyError>;
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError>;
}
impl ToWalletDescriptor for &str {
fn to_wallet_descriptor(
self,
network: Network,
) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
let descriptor = if self.contains('#') {
let parts: Vec<&str> = self.splitn(2, '#').collect();
if !get_checksum(parts[0])
@@ -87,7 +87,7 @@ impl ToWalletDescriptor for &str {
.map(|computed| computed == parts[1])
.unwrap_or(false)
{
return Err(KeyError::InvalidChecksum);
return Err(DescriptorError::InvalidDescriptorChecksum);
}
parts[0]
@@ -103,7 +103,7 @@ impl ToWalletDescriptor for &String {
fn to_wallet_descriptor(
self,
network: Network,
) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
self.as_str().to_wallet_descriptor(network)
}
}
@@ -112,7 +112,7 @@ impl ToWalletDescriptor for ExtendedDescriptor {
fn to_wallet_descriptor(
self,
network: Network,
) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
(self, KeyMap::default()).to_wallet_descriptor(network)
}
}
@@ -121,7 +121,7 @@ impl ToWalletDescriptor for (ExtendedDescriptor, KeyMap) {
fn to_wallet_descriptor(
self,
network: Network,
) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
use crate::keys::DescriptorKey;
let secp = Secp256k1::new();
@@ -140,7 +140,7 @@ impl ToWalletDescriptor for (ExtendedDescriptor, KeyMap) {
if networks.contains(&network) {
Ok(pk)
} else {
Err(KeyError::InvalidNetwork)
Err(DescriptorError::Key(KeyError::InvalidNetwork))
}
};
@@ -155,7 +155,7 @@ impl ToWalletDescriptor for DescriptorTemplateOut {
fn to_wallet_descriptor(
self,
network: Network,
) -> Result<(ExtendedDescriptor, KeyMap), KeyError> {
) -> Result<(ExtendedDescriptor, KeyMap), DescriptorError> {
let valid_networks = &self.2;
let fix_key = |pk: &DescriptorPublicKey| {
@@ -177,7 +177,7 @@ impl ToWalletDescriptor for DescriptorTemplateOut {
Ok(pk)
} else {
Err(KeyError::InvalidNetwork)
Err(DescriptorError::Key(KeyError::InvalidNetwork))
}
};
@@ -188,6 +188,22 @@ impl ToWalletDescriptor for DescriptorTemplateOut {
}
}
#[doc(hidden)]
/// Used internally mainly by the `descriptor!()` and `fragment!()` macros
pub trait CheckMiniscript<Ctx: miniscript::ScriptContext> {
fn check_minsicript(&self) -> Result<(), miniscript::Error>;
}
impl<Ctx: miniscript::ScriptContext, Pk: miniscript::MiniscriptKey> CheckMiniscript<Ctx>
for miniscript::Miniscript<Pk, Ctx>
{
fn check_minsicript(&self) -> Result<(), miniscript::Error> {
Ctx::check_global_validity(self)?;
Ok(())
}
}
/// Trait implemented on [`Descriptor`]s to add a method to extract the spending [`policy`]
pub trait ExtractPolicy {
/// Extract the spending [`policy`]
@@ -195,7 +211,7 @@ pub trait ExtractPolicy {
&self,
signers: &SignersContainer,
secp: &SecpCtx,
) -> Result<Option<Policy>, Error>;
) -> Result<Option<Policy>, DescriptorError>;
}
pub(crate) trait XKeyUtils {
@@ -235,8 +251,8 @@ impl<K: InnerXKey> XKeyUtils for DescriptorXKey<K> {
pub(crate) trait DescriptorMeta: Sized {
fn is_witness(&self) -> bool;
fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, Error>;
fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, Error>;
fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, DescriptorError>;
fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, DescriptorError>;
fn is_fixed(&self) -> bool;
fn derive_from_hd_keypaths(&self, hd_keypaths: &HDKeyPaths, secp: &SecpCtx) -> Option<Self>;
fn derive_from_psbt_input(
@@ -297,11 +313,11 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
}
}
fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, Error> {
fn get_hd_keypaths(&self, index: u32, secp: &SecpCtx) -> Result<HDKeyPaths, DescriptorError> {
let translate_key = |key: &DescriptorPublicKey,
index: u32,
paths: &mut HDKeyPaths|
-> Result<DummyKey, Error> {
-> Result<DummyKey, DescriptorError> {
match key {
DescriptorPublicKey::SinglePub(_) => {}
DescriptorPublicKey::XPub(xpub) => {
@@ -344,10 +360,10 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
Ok(answer_pk)
}
fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, Error> {
fn get_extended_keys(&self) -> Result<Vec<DescriptorXKey<ExtendedPubKey>>, DescriptorError> {
let get_key = |key: &DescriptorPublicKey,
keys: &mut Vec<DescriptorXKey<ExtendedPubKey>>|
-> Result<DummyKey, Error> {
-> Result<DummyKey, DescriptorError> {
if let DescriptorPublicKey::XPub(xpub) = key {
keys.push(xpub.clone())
}
@@ -369,7 +385,10 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
}
fn is_fixed(&self) -> bool {
fn check_key(key: &DescriptorPublicKey, flag: &mut bool) -> Result<DummyKey, Error> {
fn check_key(
key: &DescriptorPublicKey,
flag: &mut bool,
) -> Result<DummyKey, DescriptorError> {
match key {
DescriptorPublicKey::SinglePub(_) => {}
DescriptorPublicKey::XPub(xpub) => {
@@ -398,7 +417,7 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
let try_key = |key: &DescriptorPublicKey,
index: &HashMap<Fingerprint, DerivationPath>,
found_path: &mut Option<ChildNumber>|
-> Result<DummyKey, Error> {
-> Result<DummyKey, DescriptorError> {
if found_path.is_some() {
// already found a matching path, we are done
return Ok(DummyKey::default());
@@ -432,7 +451,7 @@ impl DescriptorMeta for Descriptor<DescriptorPublicKey> {
Some(path) if !xpub.is_wildcard && path.is_empty() => {
*found_path = Some(ChildNumber::Normal { index: 0 })
}
Some(_) => return Err(Error::InvalidHDKeyPath),
Some(_) => return Err(DescriptorError::InvalidHDKeyPath),
_ => {}
}
}
@@ -713,11 +732,17 @@ mod test {
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
.to_wallet_descriptor(Network::Testnet);
assert!(matches!(desc.err(), Some(KeyError::InvalidChecksum)));
assert!(matches!(
desc.err(),
Some(DescriptorError::InvalidDescriptorChecksum)
));
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)#67ju93jw"
.to_wallet_descriptor(Network::Testnet);
assert!(matches!(desc.err(), Some(KeyError::InvalidChecksum)));
assert!(matches!(
desc.err(),
Some(DescriptorError::InvalidDescriptorChecksum)
));
}
// test ToWalletDescriptor trait from &str with keys from right and wrong network
@@ -749,11 +774,17 @@ mod test {
let desc = "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/1/2/*)"
.to_wallet_descriptor(Network::Bitcoin);
assert!(matches!(desc.err(), Some(KeyError::InvalidNetwork)));
assert!(matches!(
desc.err(),
Some(DescriptorError::Key(KeyError::InvalidNetwork))
));
let desc = "wpkh(tpubD6NzVbkrYhZ4XHndKkuB8FifXm8r5FQHwrN6oZuWCz13qb93rtgKvD4PQsqC4HP4yhV3tA2fqr2RbY5mNXfM7RxXUoeABoDtsFUq2zJq6YK/1/2/*)"
.to_wallet_descriptor(Network::Bitcoin);
assert!(matches!(desc.err(), Some(KeyError::InvalidNetwork)));
assert!(matches!(
desc.err(),
Some(DescriptorError::Key(KeyError::InvalidNetwork))
));
}
// test ToWalletDescriptor trait from the output of the descriptor!() macro