diff --git a/bdk-ffi/Cargo.lock b/bdk-ffi/Cargo.lock index 491cbd3..66e871e 100644 --- a/bdk-ffi/Cargo.lock +++ b/bdk-ffi/Cargo.lock @@ -161,6 +161,7 @@ dependencies = [ "bdk", "bdk_esplora", "bdk_file_store", + "bitcoin-internals 0.2.0", "thiserror", "uniffi", ] @@ -244,6 +245,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1f9997f8650dd818369931b5672a18dbef95324d0513aa99aae758de8ce86e5b" +[[package]] +name = "bitcoin-internals" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb" + [[package]] name = "bitcoin-private" version = "0.1.0" @@ -393,7 +400,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cb1f7f2489cce83bc3bd92784f9ba5271eeb6e729b975895fc541f78cbfcdca" dependencies = [ "bitcoin", - "bitcoin-internals", + "bitcoin-internals 0.1.0", "log", "serde", "ureq", diff --git a/bdk-ffi/Cargo.toml b/bdk-ffi/Cargo.toml index 8b4b519..51e90ac 100644 --- a/bdk-ffi/Cargo.toml +++ b/bdk-ffi/Cargo.toml @@ -24,6 +24,7 @@ bdk_esplora = { version = "0.10.0", default-features = false, features = ["std", bdk_file_store = { version = "0.8.0" } uniffi = { version = "=0.26.1" } +bitcoin-internals = { version = "0.2.0", features = ["alloc"] } thiserror = "1.0.58" [build-dependencies] diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index e67060f..c09537a 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -71,6 +71,17 @@ interface AddressError { OtherAddressError(); }; +[Error] +interface TransactionError { + Io(); + OversizedVectorAllocation(); + InvalidChecksum(string expected, string actual); + NonMinimalVarInt(); + ParseFailed(); + UnsupportedSegwitFlag(u8 flag); + OtherTransactionError(); +}; + // ------------------------------------------------------------------------ // bdk crate - types module // ------------------------------------------------------------------------ @@ -394,7 +405,7 @@ interface Address { }; interface Transaction { - [Throws=Alpha3Error] + [Throws=TransactionError] constructor(sequence transaction_bytes); string txid(); diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index 0e448e9..ee83abe 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -1,4 +1,5 @@ -use crate::error::Alpha3Error; +use crate::error::{Alpha3Error, TransactionError}; +use crate::error::AddressError; use bdk::bitcoin::address::{NetworkChecked, NetworkUnchecked}; use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf; @@ -116,10 +117,9 @@ pub struct Transaction { } impl Transaction { - pub fn new(transaction_bytes: Vec) -> Result { + pub fn new(transaction_bytes: Vec) -> Result { let mut decoder = Cursor::new(transaction_bytes); - let tx: BdkTransaction = - BdkTransaction::consensus_decode(&mut decoder).map_err(|_| Alpha3Error::Generic)?; + let tx: BdkTransaction = BdkTransaction::consensus_decode(&mut decoder)?; Ok(Transaction { inner: tx }) } diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs index ed528bc..c59b885 100644 --- a/bdk-ffi/src/error.rs +++ b/bdk-ffi/src/error.rs @@ -10,6 +10,7 @@ use bdk::wallet::tx_builder::{AddUtxoError, AllowShrinkingError}; use bdk::wallet::{NewError, NewOrLoadError}; use bdk_file_store::FileError as BdkFileError; use bdk_file_store::IterError; +use bitcoin_internals::hex::display::DisplayHex; use std::convert::Infallible; #[derive(Debug, thiserror::Error)] @@ -167,6 +168,32 @@ pub enum AddressError { OtherAddressError, } +// Mapping https://docs.rs/bitcoin/latest/src/bitcoin/consensus/encode.rs.html#40-63 +#[derive(Debug, thiserror::Error)] +pub enum TransactionError { + #[error("IO error")] + Io, + + #[error("allocation of oversized vector")] + OversizedVectorAllocation, + + #[error("invalid checksum: expected={expected} actual={actual}")] + InvalidChecksum { expected: String, actual: String }, + + #[error("non-minimal varint")] + NonMinimalVarInt, + + #[error("parse failed")] + ParseFailed, + + #[error("unsupported segwit version: {flag}")] + UnsupportedSegwitFlag { flag: u8 }, + + // This is required because the bdk::bitcoin::consensus::encode::Error is non-exhaustive + #[error("other transaction error")] + OtherTransactionError, +} + impl From for WalletCreationError { fn from(error: BdkFileError) -> Self { match error { @@ -344,6 +371,30 @@ impl From for AddressError { } } +impl From for TransactionError { + fn from(error: bdk::bitcoin::consensus::encode::Error) -> Self { + match error { + bdk::bitcoin::consensus::encode::Error::Io(_) => TransactionError::Io, + bdk::bitcoin::consensus::encode::Error::OversizedVectorAllocation { .. } => { + TransactionError::OversizedVectorAllocation + } + bdk::bitcoin::consensus::encode::Error::InvalidChecksum { expected, actual } => { + let expected = DisplayHex::to_lower_hex_string(&expected); + let actual = DisplayHex::to_lower_hex_string(&actual); + TransactionError::InvalidChecksum { expected, actual } + } + bdk::bitcoin::consensus::encode::Error::NonMinimalVarInt => { + TransactionError::NonMinimalVarInt + } + bdk::bitcoin::consensus::encode::Error::ParseFailed(_) => TransactionError::ParseFailed, + bdk::bitcoin::consensus::encode::Error::UnsupportedSegwitFlag(flag) => { + TransactionError::UnsupportedSegwitFlag { flag } + } + _ => TransactionError::OtherTransactionError, + } + } +} + #[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 05f9bcc..5bf5979 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -19,6 +19,7 @@ use crate::error::CalculateFeeError; use crate::error::EsploraError; use crate::error::FeeRateError; use crate::error::PersistenceError; +use crate::error::TransactionError; use crate::error::WalletCreationError; use crate::esplora::EsploraClient; use crate::keys::DerivationPath;