diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 6720cd1..e67060f 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -52,6 +52,25 @@ enum FeeRateError { "ArithmeticOverflow" }; +[Error] +interface AddressError { + Base58(); + Bech32(); + EmptyBech32Payload(); + InvalidBech32Variant(); + InvalidWitnessVersion(u8 version); + UnparsableWitnessVersion(); + MalformedWitnessVersion(); + InvalidWitnessProgramLength(u64 length); + InvalidSegwitV0ProgramLength(u64 length); + UncompressedPubkey(); + ExcessiveScriptSize(); + UnrecognizedScript(); + UnknownAddressType(string s); + NetworkValidation(Network required, Network found, string address); + OtherAddressError(); +}; + // ------------------------------------------------------------------------ // bdk crate - types module // ------------------------------------------------------------------------ @@ -360,7 +379,7 @@ enum WordCount { }; interface Address { - [Throws=Alpha3Error] + [Throws=AddressError] constructor(string address, Network network); Network network(); diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index 261deeb..0e448e9 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -41,14 +41,9 @@ pub struct Address { } impl Address { - pub fn new(address: String, network: Network) -> Result { - let parsed_address = address - .parse::>() - .map_err(|_| Alpha3Error::Generic)?; - - let network_checked_address = parsed_address - .require_network(network) - .map_err(|_| Alpha3Error::Generic)?; + pub fn new(address: String, network: Network) -> Result { + let parsed_address = address.parse::>()?; + let network_checked_address = parsed_address.require_network(network)?; Ok(Address { inner: network_checked_address, diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs index e1412db..ed528bc 100644 --- a/bdk-ffi/src/error.rs +++ b/bdk-ffi/src/error.rs @@ -107,6 +107,66 @@ pub enum FeeRateError { ArithmeticOverflow, } +#[derive(Debug, thiserror::Error)] +pub enum AddressError { + #[error("base58 address encoding error")] + Base58, + + #[error("bech32 address encoding error")] + Bech32, + + #[error("the bech32 payload was empty")] + EmptyBech32Payload, + + #[error("invalid bech32 checksum variant found")] + InvalidBech32Variant, + + #[error("invalid witness script version: {version}")] + InvalidWitnessVersion { version: u8 }, + + #[error("incorrect format of a witness version byte")] + UnparsableWitnessVersion, + + #[error( + "bitcoin script opcode does not match any known witness version, the script is malformed" + )] + MalformedWitnessVersion, + + #[error("the witness program must be between 2 and 40 bytes in length: length={length}")] + InvalidWitnessProgramLength { length: u64 }, + + #[error("a v0 witness program must be either of length 20 or 32 bytes: length={length}")] + InvalidSegwitV0ProgramLength { length: u64 }, + + #[error("an uncompressed pubkey was used where it is not allowed")] + UncompressedPubkey, + + #[error("script size exceed 520 bytes")] + ExcessiveScriptSize, + + #[error("script is not p2pkh, p2sh, or witness program")] + UnrecognizedScript, + + #[error("unknown address type: '{s}' is either invalid or not supported")] + UnknownAddressType { s: String }, + + #[error( + "address {address} belongs to network {found} which is different from required {required}" + )] + NetworkValidation { + /// Network that was required. + required: Network, + /// Network on which the address was found to be valid. + found: Network, + /// The address itself + address: String, + }, + + // This is required because the bdk::bitcoin::address::Error is non-exhaustive + #[error("other address error")] + OtherAddressError, +} + impl From for WalletCreationError { fn from(error: BdkFileError) -> Self { match error { @@ -234,6 +294,56 @@ impl From for EsploraError { } } +impl From for AddressError { + fn from(error: bdk::bitcoin::address::Error) -> Self { + match error { + bdk::bitcoin::address::Error::Base58(_) => AddressError::Base58, + bdk::bitcoin::address::Error::Bech32(_) => AddressError::Bech32, + bdk::bitcoin::address::Error::EmptyBech32Payload => AddressError::EmptyBech32Payload, + bdk::bitcoin::address::Error::InvalidBech32Variant { .. } => { + AddressError::InvalidBech32Variant + } + bdk::bitcoin::address::Error::InvalidWitnessVersion(version) => { + AddressError::InvalidWitnessVersion { version } + } + bdk::bitcoin::address::Error::UnparsableWitnessVersion(_) => { + AddressError::UnparsableWitnessVersion + } + bdk::bitcoin::address::Error::MalformedWitnessVersion => { + AddressError::MalformedWitnessVersion + } + bdk::bitcoin::address::Error::InvalidWitnessProgramLength(length) => { + let length = length as u64; + AddressError::InvalidWitnessProgramLength { length } + } + bdk::bitcoin::address::Error::InvalidSegwitV0ProgramLength(length) => { + let length = length as u64; + AddressError::InvalidSegwitV0ProgramLength { length } + } + bdk::bitcoin::address::Error::UncompressedPubkey => AddressError::UncompressedPubkey, + bdk::bitcoin::address::Error::ExcessiveScriptSize => AddressError::ExcessiveScriptSize, + bdk::bitcoin::address::Error::UnrecognizedScript => AddressError::UnrecognizedScript, + bdk::bitcoin::address::Error::UnknownAddressType(s) => { + AddressError::UnknownAddressType { s } + } + bdk::bitcoin::address::Error::NetworkValidation { + required, + found, + address, + } => { + // let address = address.to_string(); + let address = format!("{:?}", address); + AddressError::NetworkValidation { + required, + found, + address, + } + } + _ => AddressError::OtherAddressError, + } + } +} + #[cfg(test)] mod test { use crate::error::{EsploraError, PersistenceError, WalletCreationError}; diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs index 3a28118..05f9bcc 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -13,6 +13,7 @@ use crate::bitcoin::Script; use crate::bitcoin::Transaction; use crate::bitcoin::TxOut; use crate::descriptor::Descriptor; +use crate::error::AddressError; use crate::error::Alpha3Error; use crate::error::CalculateFeeError; use crate::error::EsploraError;