Skip to content

Commit

Permalink
feat: add into_single_descriptors to descriptor
Browse files Browse the repository at this point in the history
  • Loading branch information
reez committed Nov 14, 2024
1 parent 80ca720 commit 54e929d
Show file tree
Hide file tree
Showing 4 changed files with 258 additions and 0 deletions.
48 changes: 48 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 @@ -590,6 +631,13 @@ interface Descriptor {

/// 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> into_single_descriptors();
};

// ------------------------------------------------------------------------
Expand Down
20 changes: 20 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 @@ -270,6 +272,24 @@ impl Descriptor {
pub(crate) fn is_multipath(&self) -> bool {
self.extended_descriptor.is_multipath()
}

pub(crate) fn into_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
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

0 comments on commit 54e929d

Please sign in to comment.