Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add methods to Descriptor + DescriptorPublicKey #625

Merged
merged 4 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions bdk-ffi/src/bdk.udl
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,47 @@ interface LoadWithPersistError {
CouldNotLoad();
};

[Error]
interface MiniscriptError {
AbsoluteLockTime();
AddrError(string error_message);
AddrP2shError(string error_message);
AnalysisError(string error_message);
AtOutsideOr();
BadDescriptor(string error_message);
BareDescriptorAddr();
CmsTooManyKeys(u32 keys);
ContextError(string error_message);
CouldNotSatisfy();
ExpectedChar(string char);
ImpossibleSatisfaction();
InvalidOpcode();
InvalidPush();
LiftError(string error_message);
MaxRecursiveDepthExceeded();
MissingSig();
MultiATooManyKeys(u64 keys);
MultiColon();
MultipathDescLenMismatch();
NonMinimalVerify(string error_message);
NonStandardBareScript();
NonTopLevel(string error_message);
ParseThreshold();
PolicyError(string error_message);
PubKeyCtxError();
RelativeLockTime();
Script(string error_message);
Secp(string error_message);
Threshold();
TrNoScriptCode();
Trailing(string error_message);
TypeCheck(string error_message);
Unexpected(string error_message);
UnexpectedStart();
UnknownWrapper(string char);
Unprintable(u8 byte);
};

[Error]
interface PersistenceError {
Write(string error_message);
Expand Down Expand Up @@ -736,6 +777,12 @@ interface DescriptorPublicKey {
DescriptorPublicKey extend([ByRef] DerivationPath path);

string as_string();

/// Whether or not this key has multiple derivation paths.
boolean is_multipath();

/// The fingerprint of the master key associated with this key, `0x00000000` if none.
string master_fingerprint();
};

[Traits=(Display)]
Expand Down Expand Up @@ -768,6 +815,16 @@ interface Descriptor {
constructor([ByRef] DescriptorPublicKey public_key, string fingerprint, KeychainKind keychain, Network network);

string to_string_with_secret();

/// Whether or not this key has multiple derivation paths.
boolean is_multipath();

/// Get as many descriptors as different paths in this descriptor.
///
/// For multipath descriptors it will return as many descriptors as there is
/// "parallel" paths. For regular descriptors it will just return itself.
[Throws=MiniscriptError]
sequence<Descriptor> to_single_descriptors();
};

// ------------------------------------------------------------------------
Expand Down
24 changes: 24 additions & 0 deletions bdk-ffi/src/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ use bdk_wallet::template::{
};
use bdk_wallet::KeychainKind;

use crate::error::MiniscriptError;
use std::fmt::Display;
use std::str::FromStr;
use std::sync::Arc;

#[derive(Debug)]
pub struct Descriptor {
Expand Down Expand Up @@ -266,6 +268,28 @@ impl Descriptor {
let key_map = &self.key_map;
descriptor.to_string_with_secret(key_map)
}

pub(crate) fn is_multipath(&self) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity, why are all of these access modifiers crate level? Clearly has no effect on the bindings but seems like a scope definition that doesn't actually apply

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good question/point-

self.extended_descriptor.is_multipath()
}

pub(crate) fn to_single_descriptors(&self) -> Result<Vec<Arc<Descriptor>>, MiniscriptError> {
self.extended_descriptor
.clone()
.into_single_descriptors()
.map_err(MiniscriptError::from)
.map(|descriptors| {
descriptors
.into_iter()
.map(|desc| {
Arc::new(Descriptor {
extended_descriptor: desc,
key_map: self.key_map.clone(),
})
})
.collect()
})
}
}

impl Display for Descriptor {
Expand Down
189 changes: 189 additions & 0 deletions bdk-ffi/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,120 @@ pub enum LoadWithPersistError {
CouldNotLoad,
}

#[derive(Debug, thiserror::Error)]
pub enum MiniscriptError {
#[error("absolute locktime error")]
AbsoluteLockTime,

#[error("address error: {error_message}")]
AddrError { error_message: String },

#[error("p2sh address error: {error_message}")]
AddrP2shError { error_message: String },

#[error("analysis error: {error_message}")]
AnalysisError { error_message: String },

#[error("@ found outside of OR")]
AtOutsideOr,

#[error("bad descriptor: {error_message}")]
BadDescriptor { error_message: String },

#[error("bare descriptor address")]
BareDescriptorAddr,

#[error("too many keys in checkmultisig: {keys}")]
CmsTooManyKeys { keys: u32 },

#[error("context error: {error_message}")]
ContextError { error_message: String },

#[error("could not satisfy")]
CouldNotSatisfy,

#[error("expected character: {char}")]
ExpectedChar { char: String },

#[error("impossible satisfaction")]
ImpossibleSatisfaction,

#[error("invalid opcode")]
InvalidOpcode,

#[error("invalid push")]
InvalidPush,

#[error("lift error: {error_message}")]
LiftError { error_message: String },

#[error("maximum recursive depth exceeded")]
MaxRecursiveDepthExceeded,

#[error("missing signature")]
MissingSig,

#[error("too many keys in multi-a: {keys}")]
MultiATooManyKeys { keys: u64 },

#[error("multiple colons in fragment name")]
MultiColon,

#[error("multipath descriptor length mismatch")]
MultipathDescLenMismatch,

#[error("non-minimal verify: {error_message}")]
NonMinimalVerify { error_message: String },

#[error("non-standard bare script")]
NonStandardBareScript,

#[error("non top-level: {error_message}")]
NonTopLevel { error_message: String },

#[error("parse threshold error")]
ParseThreshold,

#[error("policy error: {error_message}")]
PolicyError { error_message: String },

#[error("pubkey context error")]
PubKeyCtxError,

#[error("relative locktime error")]
RelativeLockTime,

#[error("script error: {error_message}")]
Script { error_message: String },

#[error("secp256k1 error: {error_message}")]
Secp { error_message: String },

#[error("threshold error")]
Threshold,

#[error("no script code for taproot")]
TrNoScriptCode,

#[error("trailing data: {error_message}")]
Trailing { error_message: String },

#[error("type check error: {error_message}")]
TypeCheck { error_message: String },

#[error("unexpected: {error_message}")]
Unexpected { error_message: String },

#[error("unexpected start")]
UnexpectedStart,

#[error("unknown wrapper: {char}")]
UnknownWrapper { char: String },

#[error("unprintable character: {byte}")]
Unprintable { byte: u8 },
}

#[derive(Debug, thiserror::Error)]
pub enum PersistenceError {
#[error("writing to persistence error: {error_message}")]
Expand Down Expand Up @@ -1063,6 +1177,81 @@ impl From<BdkLoadWithPersistError<chain::rusqlite::Error>> for LoadWithPersistEr
}
}

impl From<bdk_wallet::miniscript::Error> for MiniscriptError {
fn from(error: bdk_wallet::miniscript::Error) -> Self {
use bdk_wallet::miniscript::Error as BdkMiniscriptError;
match error {
BdkMiniscriptError::AbsoluteLockTime(_) => MiniscriptError::AbsoluteLockTime,
BdkMiniscriptError::AddrError(e) => MiniscriptError::AddrError {
error_message: e.to_string(),
},
BdkMiniscriptError::AddrP2shError(e) => MiniscriptError::AddrP2shError {
error_message: e.to_string(),
},
BdkMiniscriptError::AnalysisError(e) => MiniscriptError::AnalysisError {
error_message: e.to_string(),
},
BdkMiniscriptError::AtOutsideOr(_) => MiniscriptError::AtOutsideOr,
BdkMiniscriptError::BadDescriptor(s) => {
MiniscriptError::BadDescriptor { error_message: s }
}
BdkMiniscriptError::BareDescriptorAddr => MiniscriptError::BareDescriptorAddr,
BdkMiniscriptError::CmsTooManyKeys(n) => MiniscriptError::CmsTooManyKeys { keys: n },
BdkMiniscriptError::ContextError(e) => MiniscriptError::ContextError {
error_message: e.to_string(),
},
BdkMiniscriptError::CouldNotSatisfy => MiniscriptError::CouldNotSatisfy,
BdkMiniscriptError::ExpectedChar(c) => MiniscriptError::ExpectedChar {
char: c.to_string(),
},
BdkMiniscriptError::ImpossibleSatisfaction => MiniscriptError::ImpossibleSatisfaction,
BdkMiniscriptError::InvalidOpcode(_) => MiniscriptError::InvalidOpcode,
BdkMiniscriptError::InvalidPush(_) => MiniscriptError::InvalidPush,
BdkMiniscriptError::LiftError(e) => MiniscriptError::LiftError {
error_message: e.to_string(),
},
BdkMiniscriptError::MaxRecursiveDepthExceeded => {
MiniscriptError::MaxRecursiveDepthExceeded
}
BdkMiniscriptError::MissingSig(_) => MiniscriptError::MissingSig,
BdkMiniscriptError::MultiATooManyKeys(n) => {
MiniscriptError::MultiATooManyKeys { keys: n }
}
BdkMiniscriptError::MultiColon(_) => MiniscriptError::MultiColon,
BdkMiniscriptError::MultipathDescLenMismatch => {
MiniscriptError::MultipathDescLenMismatch
}
BdkMiniscriptError::NonMinimalVerify(s) => {
MiniscriptError::NonMinimalVerify { error_message: s }
}
BdkMiniscriptError::NonStandardBareScript => MiniscriptError::NonStandardBareScript,
BdkMiniscriptError::NonTopLevel(s) => MiniscriptError::NonTopLevel { error_message: s },
BdkMiniscriptError::ParseThreshold(_) => MiniscriptError::ParseThreshold,
BdkMiniscriptError::PolicyError(e) => MiniscriptError::PolicyError {
error_message: e.to_string(),
},
BdkMiniscriptError::PubKeyCtxError(_, _) => MiniscriptError::PubKeyCtxError,
BdkMiniscriptError::RelativeLockTime(_) => MiniscriptError::RelativeLockTime,
BdkMiniscriptError::Script(e) => MiniscriptError::Script {
error_message: e.to_string(),
},
BdkMiniscriptError::Secp(e) => MiniscriptError::Secp {
error_message: e.to_string(),
},
BdkMiniscriptError::Threshold(_) => MiniscriptError::Threshold,
BdkMiniscriptError::TrNoScriptCode => MiniscriptError::TrNoScriptCode,
BdkMiniscriptError::Trailing(s) => MiniscriptError::Trailing { error_message: s },
BdkMiniscriptError::TypeCheck(s) => MiniscriptError::TypeCheck { error_message: s },
BdkMiniscriptError::Unexpected(s) => MiniscriptError::Unexpected { error_message: s },
BdkMiniscriptError::UnexpectedStart => MiniscriptError::UnexpectedStart,
BdkMiniscriptError::UnknownWrapper(c) => MiniscriptError::UnknownWrapper {
char: c.to_string(),
},
BdkMiniscriptError::Unprintable(b) => MiniscriptError::Unprintable { byte: b },
}
}
}

impl From<std::io::Error> for PersistenceError {
fn from(error: std::io::Error) -> Self {
PersistenceError::Write {
Expand Down
8 changes: 8 additions & 0 deletions bdk-ffi/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,14 @@ impl DescriptorPublicKey {
pub(crate) fn as_string(&self) -> String {
self.0.to_string()
}

pub(crate) fn is_multipath(&self) -> bool {
self.0.is_multipath()
}

pub(crate) fn master_fingerprint(&self) -> String {
self.0.master_fingerprint().to_string()
}
}

#[cfg(test)]
Expand Down
1 change: 1 addition & 0 deletions bdk-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use crate::error::EsploraError;
use crate::error::ExtractTxError;
use crate::error::FromScriptError;
use crate::error::LoadWithPersistError;
use crate::error::MiniscriptError;
use crate::error::PersistenceError;
use crate::error::PsbtError;
use crate::error::PsbtParseError;
Expand Down
Loading