feat: add esplora error

This commit is contained in:
Matthew 2024-03-06 10:09:46 -06:00 committed by GitHub
parent 43c1ca66b8
commit b249dae875
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 159 additions and 5 deletions

View File

@ -27,6 +27,21 @@ interface WalletCreationError {
LoadedNetworkDoesNotMatch(Network expected, Network? got); LoadedNetworkDoesNotMatch(Network expected, Network? got);
}; };
[Error]
interface EsploraError {
Ureq(string error_message);
UreqTransport(string error_message);
Http(u16 status_code);
Io(string error_message);
NoHeader();
Parsing(string error_message);
BitcoinEncoding(string error_message);
Hex(string error_message);
TransactionNotFound();
HeaderHeightNotFound(u32 height);
HeaderHashNotFound();
};
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
// bdk crate - types module // bdk crate - types module
// ------------------------------------------------------------------------ // ------------------------------------------------------------------------
@ -274,7 +289,7 @@ interface Descriptor {
interface EsploraClient { interface EsploraClient {
constructor(string url); constructor(string url);
[Throws=Alpha3Error] [Throws=EsploraError]
Update full_scan(Wallet wallet, u64 stop_gap, u64 parallel_requests); Update full_scan(Wallet wallet, u64 stop_gap, u64 parallel_requests);
[Throws=Alpha3Error] [Throws=Alpha3Error]

View File

@ -1,6 +1,7 @@
use crate::bitcoin::OutPoint; use crate::bitcoin::OutPoint;
use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError; use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError;
use bdk_esplora::esplora_client::Error as BdkEsploraError;
use std::fmt; use std::fmt;
@ -186,8 +187,88 @@ impl From<BdkCalculateFeeError> for CalculateFeeError {
impl std::error::Error for CalculateFeeError {} impl std::error::Error for CalculateFeeError {}
#[derive(Debug)]
pub enum EsploraError {
Ureq { error_message: String },
UreqTransport { error_message: String },
Http { status_code: u16 },
Io { error_message: String },
NoHeader,
Parsing { error_message: String },
BitcoinEncoding { error_message: String },
Hex { error_message: String },
TransactionNotFound,
HeaderHeightNotFound { height: u32 },
HeaderHashNotFound,
}
impl fmt::Display for EsploraError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
EsploraError::Ureq { error_message } => write!(f, "Ureq error: {}", error_message),
EsploraError::UreqTransport { error_message } => {
write!(f, "Ureq transport error: {}", error_message)
}
EsploraError::Http { status_code } => {
write!(f, "HTTP error with status code: {}", status_code)
}
EsploraError::Io { error_message } => write!(f, "IO error: {}", error_message),
EsploraError::NoHeader => write!(f, "No header found in the response"),
EsploraError::Parsing { error_message } => {
write!(f, "Parsing error: {}", error_message)
}
EsploraError::BitcoinEncoding { error_message } => {
write!(f, "Bitcoin encoding error: {}", error_message)
}
EsploraError::Hex { error_message } => {
write!(f, "Hex decoding error: {}", error_message)
}
EsploraError::TransactionNotFound => write!(f, "Transaction not found"),
EsploraError::HeaderHeightNotFound { height } => {
write!(f, "Header height {} not found", height)
}
EsploraError::HeaderHashNotFound => write!(f, "Header hash not found"),
}
}
}
impl From<BdkEsploraError> for EsploraError {
fn from(error: BdkEsploraError) -> Self {
match error {
BdkEsploraError::Ureq(e) => EsploraError::Ureq {
error_message: e.to_string(),
},
BdkEsploraError::UreqTransport(e) => EsploraError::UreqTransport {
error_message: e.to_string(),
},
BdkEsploraError::HttpResponse(code) => EsploraError::Http { status_code: code },
BdkEsploraError::Io(e) => EsploraError::Io {
error_message: e.to_string(),
},
BdkEsploraError::NoHeader => EsploraError::NoHeader,
BdkEsploraError::Parsing(e) => EsploraError::Parsing {
error_message: e.to_string(),
},
BdkEsploraError::BitcoinEncoding(e) => EsploraError::BitcoinEncoding {
error_message: e.to_string(),
},
BdkEsploraError::Hex(e) => EsploraError::Hex {
error_message: e.to_string(),
},
BdkEsploraError::TransactionNotFound(_) => EsploraError::TransactionNotFound,
BdkEsploraError::HeaderHeightNotFound(height) => {
EsploraError::HeaderHeightNotFound { height }
}
BdkEsploraError::HeaderHashNotFound(_) => EsploraError::HeaderHashNotFound,
}
}
}
impl std::error::Error for EsploraError {}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::error::EsploraError;
use crate::CalculateFeeError; use crate::CalculateFeeError;
use crate::OutPoint; use crate::OutPoint;
@ -231,4 +312,61 @@ mod test {
assert_eq!(error.to_string(), "Negative fee value: -100"); assert_eq!(error.to_string(), "Negative fee value: -100");
} }
#[test]
fn test_esplora_errors() {
let cases = vec![
(
EsploraError::Ureq {
error_message: "Network error".to_string(),
},
"Ureq error: Network error",
),
(
EsploraError::UreqTransport {
error_message: "Timeout occurred".to_string(),
},
"Ureq transport error: Timeout occurred",
),
(
EsploraError::Http { status_code: 404 },
"HTTP error with status code: 404",
),
(
EsploraError::Io {
error_message: "File not found".to_string(),
},
"IO error: File not found",
),
(EsploraError::NoHeader, "No header found in the response"),
(
EsploraError::Parsing {
error_message: "Invalid JSON".to_string(),
},
"Parsing error: Invalid JSON",
),
(
EsploraError::BitcoinEncoding {
error_message: "Bad format".to_string(),
},
"Bitcoin encoding error: Bad format",
),
(
EsploraError::Hex {
error_message: "Invalid hex".to_string(),
},
"Hex decoding error: Invalid hex",
),
(EsploraError::TransactionNotFound, "Transaction not found"),
(
EsploraError::HeaderHeightNotFound { height: 123456 },
"Header height 123456 not found",
),
(EsploraError::HeaderHashNotFound, "Header hash not found"),
];
for (error, expected_message) in cases {
assert_eq!(error.to_string(), expected_message);
}
}
} }

View File

@ -1,4 +1,4 @@
use crate::error::Alpha3Error; use crate::error::{Alpha3Error, EsploraError};
use crate::wallet::{Update, Wallet}; use crate::wallet::{Update, Wallet};
use bdk::bitcoin::Transaction as BdkTransaction; use bdk::bitcoin::Transaction as BdkTransaction;
@ -24,7 +24,7 @@ impl EsploraClient {
wallet: Arc<Wallet>, wallet: Arc<Wallet>,
stop_gap: u64, stop_gap: u64,
parallel_requests: u64, parallel_requests: u64,
) -> Result<Arc<Update>, Alpha3Error> { ) -> Result<Arc<Update>, EsploraError> {
let wallet = wallet.get_wallet(); let wallet = wallet.get_wallet();
let previous_tip = wallet.latest_checkpoint(); let previous_tip = wallet.latest_checkpoint();
@ -33,13 +33,13 @@ impl EsploraClient {
let (update_graph, last_active_indices) = self let (update_graph, last_active_indices) = self
.0 .0
.full_scan(keychain_spks, stop_gap as usize, parallel_requests as usize) .full_scan(keychain_spks, stop_gap as usize, parallel_requests as usize)
.unwrap(); .map_err(|e| EsploraError::from(*e))?;
let missing_heights = update_graph.missing_heights(wallet.local_chain()); let missing_heights = update_graph.missing_heights(wallet.local_chain());
let chain_update = self let chain_update = self
.0 .0
.update_local_chain(previous_tip, missing_heights) .update_local_chain(previous_tip, missing_heights)
.unwrap(); .map_err(|e| EsploraError::from(*e))?;
let update = BdkUpdate { let update = BdkUpdate {
last_active_indices, last_active_indices,

View File

@ -15,6 +15,7 @@ use crate::bitcoin::TxOut;
use crate::descriptor::Descriptor; use crate::descriptor::Descriptor;
use crate::error::Alpha3Error; use crate::error::Alpha3Error;
use crate::error::CalculateFeeError; use crate::error::CalculateFeeError;
use crate::error::EsploraError;
use crate::esplora::EsploraClient; use crate::esplora::EsploraClient;
use crate::keys::DerivationPath; use crate::keys::DerivationPath;
use crate::keys::DescriptorPublicKey; use crate::keys::DescriptorPublicKey;