From 23bb9a937123883d406d1ca599bf10441f727bff Mon Sep 17 00:00:00 2001 From: Matthew Date: Sun, 21 Jun 2026 10:48:34 -0500 Subject: [PATCH] fix: return psbt spend utxo errors --- bdk-ffi/src/bitcoin.rs | 10 +++++++--- bdk-ffi/src/error.rs | 25 +++++++++++++++++++++++++ bdk-ffi/src/tests/bitcoin.rs | 11 ++++++++++- bdk-ffi/src/tests/error.rs | 17 +++++++++++++++++ 4 files changed, 59 insertions(+), 4 deletions(-) diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index 223f874ce..98dbe7c83 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -1507,10 +1507,14 @@ impl Psbt { } /// Returns the spending utxo for this PSBT's input at `input_index`. - pub fn spend_utxo(&self, input_index: u64) -> String { + pub fn spend_utxo(&self, input_index: u64) -> Result { let psbt = self.0.lock().unwrap(); - let utxo = psbt.spend_utxo(input_index as usize).unwrap(); - serde_json::to_string(&utxo).unwrap() + let utxo = psbt + .spend_utxo(input_index as usize) + .map_err(PsbtError::from)?; + serde_json::to_string(&utxo).map_err(|error| PsbtError::Serialization { + error_message: error.to_string(), + }) } /// The corresponding key-value map for each input in the unsigned transaction. diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs index 4f718a76a..1d02b1faf 100644 --- a/bdk-ffi/src/error.rs +++ b/bdk-ffi/src/error.rs @@ -12,6 +12,7 @@ use bdk_wallet::bitcoin::hex::DisplayHex; use bdk_wallet::bitcoin::psbt::Error as BdkPsbtError; use bdk_wallet::bitcoin::psbt::ExtractTxError as BdkExtractTxError; use bdk_wallet::bitcoin::psbt::PsbtParseError as BdkPsbtParseError; +use bdk_wallet::bitcoin::psbt::SignError as BdkPsbtSignError; use bdk_wallet::bitcoin::script::PushBytesError; use bdk_wallet::chain::local_chain::CannotConnectError as BdkCannotConnectError; use bdk_wallet::chain::rusqlite::Error as BdkSqliteError; @@ -618,6 +619,18 @@ pub enum PsbtError { #[error("output index is out of bounds of non witness script output array")] PsbtUtxoOutOfBounds, + #[error("input index is out of bounds")] + InputIndexOutOfBounds, + + #[error("missing spend UTXO in PSBT")] + MissingSpendUtxo, + + #[error("PSBT serialization error: {error_message}")] + Serialization { error_message: String }, + + #[error("PSBT spend UTXO error: {error_message}")] + SpendUtxo { error_message: String }, + #[error("invalid key: {key}")] InvalidKey { key: String }, @@ -1542,6 +1555,18 @@ impl From for PsbtError { } } +impl From for PsbtError { + fn from(error: BdkPsbtSignError) -> Self { + match error { + BdkPsbtSignError::IndexOutOfBounds(_) => PsbtError::InputIndexOutOfBounds, + BdkPsbtSignError::MissingSpendUtxo => PsbtError::MissingSpendUtxo, + _ => PsbtError::SpendUtxo { + error_message: error.to_string(), + }, + } + } +} + impl From for PsbtParseError { fn from(error: BdkPsbtParseError) -> Self { match error { diff --git a/bdk-ffi/src/tests/bitcoin.rs b/bdk-ffi/src/tests/bitcoin.rs index db83f2654..54a150cea 100644 --- a/bdk-ffi/src/tests/bitcoin.rs +++ b/bdk-ffi/src/tests/bitcoin.rs @@ -1,4 +1,5 @@ use crate::bitcoin::{Address, AddressData, Key, Network, ProprietaryKey, Psbt}; +use crate::error::PsbtError; use bdk_electrum::bdk_core::bitcoin::hex::DisplayHex; #[test] @@ -360,7 +361,7 @@ fn test_to_address_data() { #[test] fn test_psbt_spend_utxo() { let psbt = sample_psbt(); - let psbt_utxo = psbt.spend_utxo(0); + let psbt_utxo = psbt.spend_utxo(0).unwrap(); assert_eq!( psbt_utxo, @@ -369,6 +370,14 @@ fn test_psbt_spend_utxo() { ); } +#[test] +fn test_psbt_spend_utxo_invalid_index_returns_error() { + let psbt = sample_psbt(); + let error = psbt.spend_utxo(1).unwrap_err(); + + assert!(matches!(error, PsbtError::InputIndexOutOfBounds)); +} + #[test] fn test_psbt_input_length() { let psbt = sample_psbt2(); diff --git a/bdk-ffi/src/tests/error.rs b/bdk-ffi/src/tests/error.rs index d130696d9..c206e3bd6 100644 --- a/bdk-ffi/src/tests/error.rs +++ b/bdk-ffi/src/tests/error.rs @@ -398,6 +398,23 @@ fn test_error_psbt() { PsbtError::PsbtUtxoOutOfBounds, "output index is out of bounds of non witness script output array", ), + ( + PsbtError::InputIndexOutOfBounds, + "input index is out of bounds", + ), + (PsbtError::MissingSpendUtxo, "missing spend UTXO in PSBT"), + ( + PsbtError::Serialization { + error_message: "serialization error".to_string(), + }, + "PSBT serialization error: serialization error", + ), + ( + PsbtError::SpendUtxo { + error_message: "spend error".to_string(), + }, + "PSBT spend UTXO error: spend error", + ), ( PsbtError::InvalidKey { key: "key".to_string(),