refactor: streamline blockchain clients error names
This commit is contained in:
parent
8f4c80cb98
commit
ca8a3d0471
@ -102,7 +102,7 @@ interface DescriptorKeyError {
|
|||||||
};
|
};
|
||||||
|
|
||||||
[Error]
|
[Error]
|
||||||
interface ElectrumClientError {
|
interface ElectrumError {
|
||||||
IOError(string error_message);
|
IOError(string error_message);
|
||||||
Json(string error_message);
|
Json(string error_message);
|
||||||
Hex(string error_message);
|
Hex(string error_message);
|
||||||
@ -493,16 +493,16 @@ interface EsploraClient {
|
|||||||
// ------------------------------------------------------------------------
|
// ------------------------------------------------------------------------
|
||||||
|
|
||||||
interface ElectrumClient {
|
interface ElectrumClient {
|
||||||
[Throws=ElectrumClientError]
|
[Throws=ElectrumError]
|
||||||
constructor(string url);
|
constructor(string url);
|
||||||
|
|
||||||
[Throws=ElectrumClientError]
|
[Throws=ElectrumError]
|
||||||
Update full_scan(FullScanRequest full_scan_request, u64 stop_gap, u64 batch_size, boolean fetch_prev_txouts);
|
Update full_scan(FullScanRequest full_scan_request, u64 stop_gap, u64 batch_size, boolean fetch_prev_txouts);
|
||||||
|
|
||||||
[Throws=ElectrumClientError]
|
[Throws=ElectrumError]
|
||||||
Update sync(SyncRequest sync_request, u64 batch_size, boolean fetch_prev_txouts);
|
Update sync(SyncRequest sync_request, u64 batch_size, boolean fetch_prev_txouts);
|
||||||
|
|
||||||
[Throws=ElectrumClientError]
|
[Throws=ElectrumError]
|
||||||
string broadcast([ByRef] Transaction transaction);
|
string broadcast([ByRef] Transaction transaction);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::error::ElectrumClientError;
|
use crate::bitcoin::Transaction;
|
||||||
|
use crate::error::ElectrumError;
|
||||||
use crate::types::{FullScanRequest, SyncRequest};
|
use crate::types::{FullScanRequest, SyncRequest};
|
||||||
use crate::wallet::Update;
|
use crate::wallet::Update;
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use bdk::bitcoin::Transaction as BdkTransaction;
|
use bdk::bitcoin::Transaction as BdkTransaction;
|
||||||
use bdk::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
use bdk::chain::spk_client::FullScanRequest as BdkFullScanRequest;
|
||||||
@ -12,13 +12,13 @@ use bdk::KeychainKind;
|
|||||||
use bdk_electrum::electrum_client::{Client as BdkBlockingClient, ElectrumApi};
|
use bdk_electrum::electrum_client::{Client as BdkBlockingClient, ElectrumApi};
|
||||||
use bdk_electrum::{ElectrumExt, ElectrumFullScanResult, ElectrumSyncResult};
|
use bdk_electrum::{ElectrumExt, ElectrumFullScanResult, ElectrumSyncResult};
|
||||||
|
|
||||||
use crate::bitcoin::Transaction;
|
use std::collections::BTreeMap;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct ElectrumClient(BdkBlockingClient);
|
pub struct ElectrumClient(BdkBlockingClient);
|
||||||
|
|
||||||
impl ElectrumClient {
|
impl ElectrumClient {
|
||||||
pub fn new(url: String) -> Result<Self, ElectrumClientError> {
|
pub fn new(url: String) -> Result<Self, ElectrumError> {
|
||||||
let client = BdkBlockingClient::new(url.as_str())?;
|
let client = BdkBlockingClient::new(url.as_str())?;
|
||||||
Ok(Self(client))
|
Ok(Self(client))
|
||||||
}
|
}
|
||||||
@ -29,14 +29,14 @@ impl ElectrumClient {
|
|||||||
stop_gap: u64,
|
stop_gap: u64,
|
||||||
batch_size: u64,
|
batch_size: u64,
|
||||||
fetch_prev_txouts: bool,
|
fetch_prev_txouts: bool,
|
||||||
) -> Result<Arc<Update>, ElectrumClientError> {
|
) -> Result<Arc<Update>, ElectrumError> {
|
||||||
// using option and take is not ideal but the only way to take full ownership of the request
|
// using option and take is not ideal but the only way to take full ownership of the request
|
||||||
let request: BdkFullScanRequest<KeychainKind> = request
|
let request: BdkFullScanRequest<KeychainKind> = request
|
||||||
.0
|
.0
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.take()
|
.take()
|
||||||
.ok_or(ElectrumClientError::RequestAlreadyConsumed)?;
|
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
|
||||||
|
|
||||||
let electrum_result: ElectrumFullScanResult<KeychainKind> = self.0.full_scan(
|
let electrum_result: ElectrumFullScanResult<KeychainKind> = self.0.full_scan(
|
||||||
request,
|
request,
|
||||||
@ -61,14 +61,14 @@ impl ElectrumClient {
|
|||||||
request: Arc<SyncRequest>,
|
request: Arc<SyncRequest>,
|
||||||
batch_size: u64,
|
batch_size: u64,
|
||||||
fetch_prev_txouts: bool,
|
fetch_prev_txouts: bool,
|
||||||
) -> Result<Arc<Update>, ElectrumClientError> {
|
) -> Result<Arc<Update>, ElectrumError> {
|
||||||
// using option and take is not ideal but the only way to take full ownership of the request
|
// using option and take is not ideal but the only way to take full ownership of the request
|
||||||
let request: BdkSyncRequest = request
|
let request: BdkSyncRequest = request
|
||||||
.0
|
.0
|
||||||
.lock()
|
.lock()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.take()
|
.take()
|
||||||
.ok_or(ElectrumClientError::RequestAlreadyConsumed)?;
|
.ok_or(ElectrumError::RequestAlreadyConsumed)?;
|
||||||
|
|
||||||
let electrum_result: ElectrumSyncResult =
|
let electrum_result: ElectrumSyncResult =
|
||||||
self.0
|
self.0
|
||||||
@ -85,11 +85,11 @@ impl ElectrumClient {
|
|||||||
Ok(Arc::new(Update(update)))
|
Ok(Arc::new(Update(update)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn broadcast(&self, transaction: &Transaction) -> Result<String, ElectrumClientError> {
|
pub fn broadcast(&self, transaction: &Transaction) -> Result<String, ElectrumError> {
|
||||||
let bdk_transaction: BdkTransaction = transaction.into();
|
let bdk_transaction: BdkTransaction = transaction.into();
|
||||||
self.0
|
self.0
|
||||||
.transaction_broadcast(&bdk_transaction)
|
.transaction_broadcast(&bdk_transaction)
|
||||||
.map_err(ElectrumClientError::from)
|
.map_err(ElectrumError::from)
|
||||||
.map(|txid| txid.to_string())
|
.map(|txid| txid.to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,7 @@ pub enum DescriptorKeyError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, thiserror::Error)]
|
||||||
pub enum ElectrumClientError {
|
pub enum ElectrumError {
|
||||||
#[error("{error_message}")]
|
#[error("{error_message}")]
|
||||||
IOError { error_message: String },
|
IOError { error_message: String },
|
||||||
|
|
||||||
@ -558,44 +558,44 @@ impl From<BdkAddressError> for AddressError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<BdkElectrumError> for ElectrumClientError {
|
impl From<BdkElectrumError> for ElectrumError {
|
||||||
fn from(error: BdkElectrumError) -> Self {
|
fn from(error: BdkElectrumError) -> Self {
|
||||||
match error {
|
match error {
|
||||||
BdkElectrumError::IOError(e) => ElectrumClientError::IOError {
|
BdkElectrumError::IOError(e) => ElectrumError::IOError {
|
||||||
error_message: e.to_string(),
|
error_message: e.to_string(),
|
||||||
},
|
},
|
||||||
BdkElectrumError::JSON(e) => ElectrumClientError::Json {
|
BdkElectrumError::JSON(e) => ElectrumError::Json {
|
||||||
error_message: e.to_string(),
|
error_message: e.to_string(),
|
||||||
},
|
},
|
||||||
BdkElectrumError::Hex(e) => ElectrumClientError::Hex {
|
BdkElectrumError::Hex(e) => ElectrumError::Hex {
|
||||||
error_message: e.to_string(),
|
error_message: e.to_string(),
|
||||||
},
|
},
|
||||||
BdkElectrumError::Protocol(e) => ElectrumClientError::Protocol {
|
BdkElectrumError::Protocol(e) => ElectrumError::Protocol {
|
||||||
error_message: e.to_string(),
|
error_message: e.to_string(),
|
||||||
},
|
},
|
||||||
BdkElectrumError::Bitcoin(e) => ElectrumClientError::Bitcoin {
|
BdkElectrumError::Bitcoin(e) => ElectrumError::Bitcoin {
|
||||||
error_message: e.to_string(),
|
error_message: e.to_string(),
|
||||||
},
|
},
|
||||||
BdkElectrumError::AlreadySubscribed(_) => ElectrumClientError::AlreadySubscribed,
|
BdkElectrumError::AlreadySubscribed(_) => ElectrumError::AlreadySubscribed,
|
||||||
BdkElectrumError::NotSubscribed(_) => ElectrumClientError::NotSubscribed,
|
BdkElectrumError::NotSubscribed(_) => ElectrumError::NotSubscribed,
|
||||||
BdkElectrumError::InvalidResponse(e) => ElectrumClientError::InvalidResponse {
|
BdkElectrumError::InvalidResponse(e) => ElectrumError::InvalidResponse {
|
||||||
error_message: e.to_string(),
|
error_message: e.to_string(),
|
||||||
},
|
},
|
||||||
BdkElectrumError::Message(e) => ElectrumClientError::Message {
|
BdkElectrumError::Message(e) => ElectrumError::Message {
|
||||||
error_message: e.to_string(),
|
error_message: e.to_string(),
|
||||||
},
|
},
|
||||||
BdkElectrumError::InvalidDNSNameError(domain) => {
|
BdkElectrumError::InvalidDNSNameError(domain) => {
|
||||||
ElectrumClientError::InvalidDNSNameError { domain }
|
ElectrumError::InvalidDNSNameError { domain }
|
||||||
}
|
}
|
||||||
BdkElectrumError::MissingDomain => ElectrumClientError::MissingDomain,
|
BdkElectrumError::MissingDomain => ElectrumError::MissingDomain,
|
||||||
BdkElectrumError::AllAttemptsErrored(_) => ElectrumClientError::AllAttemptsErrored,
|
BdkElectrumError::AllAttemptsErrored(_) => ElectrumError::AllAttemptsErrored,
|
||||||
BdkElectrumError::SharedIOError(e) => ElectrumClientError::SharedIOError {
|
BdkElectrumError::SharedIOError(e) => ElectrumError::SharedIOError {
|
||||||
error_message: e.to_string(),
|
error_message: e.to_string(),
|
||||||
},
|
},
|
||||||
BdkElectrumError::CouldntLockReader => ElectrumClientError::CouldntLockReader,
|
BdkElectrumError::CouldntLockReader => ElectrumError::CouldntLockReader,
|
||||||
BdkElectrumError::Mpsc => ElectrumClientError::Mpsc,
|
BdkElectrumError::Mpsc => ElectrumError::Mpsc,
|
||||||
BdkElectrumError::CouldNotCreateConnection(error_message) => {
|
BdkElectrumError::CouldNotCreateConnection(error_message) => {
|
||||||
ElectrumClientError::CouldNotCreateConnection {
|
ElectrumError::CouldNotCreateConnection {
|
||||||
error_message: error_message.to_string(),
|
error_message: error_message.to_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1079,7 +1079,7 @@ impl From<NewOrLoadError> for WalletCreationError {
|
|||||||
mod test {
|
mod test {
|
||||||
use crate::error::{
|
use crate::error::{
|
||||||
AddressError, Bip32Error, Bip39Error, CannotConnectError, CreateTxError, DescriptorError,
|
AddressError, Bip32Error, Bip39Error, CannotConnectError, CreateTxError, DescriptorError,
|
||||||
DescriptorKeyError, ElectrumClientError, EsploraError, ExtractTxError, FeeRateError,
|
DescriptorKeyError, ElectrumError, EsploraError, ExtractTxError, FeeRateError,
|
||||||
ParseAmountError, PersistenceError, PsbtParseError, TransactionError, TxidParseError,
|
ParseAmountError, PersistenceError, PsbtParseError, TransactionError, TxidParseError,
|
||||||
WalletCreationError,
|
WalletCreationError,
|
||||||
};
|
};
|
||||||
@ -1488,77 +1488,77 @@ mod test {
|
|||||||
fn test_error_electrum_client() {
|
fn test_error_electrum_client() {
|
||||||
let cases = vec![
|
let cases = vec![
|
||||||
(
|
(
|
||||||
ElectrumClientError::IOError { error_message: "message".to_string(), },
|
ElectrumError::IOError { error_message: "message".to_string(), },
|
||||||
"message",
|
"message",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::Json { error_message: "message".to_string(), },
|
ElectrumError::Json { error_message: "message".to_string(), },
|
||||||
"message",
|
"message",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::Hex { error_message: "message".to_string(), },
|
ElectrumError::Hex { error_message: "message".to_string(), },
|
||||||
"message",
|
"message",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::Protocol { error_message: "message".to_string(), },
|
ElectrumError::Protocol { error_message: "message".to_string(), },
|
||||||
"electrum server error: message",
|
"electrum server error: message",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::Bitcoin {
|
ElectrumError::Bitcoin {
|
||||||
error_message: "message".to_string(),
|
error_message: "message".to_string(),
|
||||||
},
|
},
|
||||||
"message",
|
"message",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::AlreadySubscribed,
|
ElectrumError::AlreadySubscribed,
|
||||||
"already subscribed to the notifications of an address",
|
"already subscribed to the notifications of an address",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::NotSubscribed,
|
ElectrumError::NotSubscribed,
|
||||||
"not subscribed to the notifications of an address",
|
"not subscribed to the notifications of an address",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::InvalidResponse {
|
ElectrumError::InvalidResponse {
|
||||||
error_message: "message".to_string(),
|
error_message: "message".to_string(),
|
||||||
},
|
},
|
||||||
"error during the deserialization of a response from the server: message",
|
"error during the deserialization of a response from the server: message",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::Message {
|
ElectrumError::Message {
|
||||||
error_message: "message".to_string(),
|
error_message: "message".to_string(),
|
||||||
},
|
},
|
||||||
"message",
|
"message",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::InvalidDNSNameError {
|
ElectrumError::InvalidDNSNameError {
|
||||||
domain: "domain".to_string(),
|
domain: "domain".to_string(),
|
||||||
},
|
},
|
||||||
"invalid domain name domain not matching SSL certificate",
|
"invalid domain name domain not matching SSL certificate",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::MissingDomain,
|
ElectrumError::MissingDomain,
|
||||||
"missing domain while it was explicitly asked to validate it",
|
"missing domain while it was explicitly asked to validate it",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::AllAttemptsErrored,
|
ElectrumError::AllAttemptsErrored,
|
||||||
"made one or multiple attempts, all errored",
|
"made one or multiple attempts, all errored",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::SharedIOError {
|
ElectrumError::SharedIOError {
|
||||||
error_message: "message".to_string(),
|
error_message: "message".to_string(),
|
||||||
},
|
},
|
||||||
"message",
|
"message",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::CouldntLockReader,
|
ElectrumError::CouldntLockReader,
|
||||||
"couldn't take a lock on the reader mutex. This means that there's already another reader thread is running"
|
"couldn't take a lock on the reader mutex. This means that there's already another reader thread is running"
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::Mpsc,
|
ElectrumError::Mpsc,
|
||||||
"broken IPC communication channel: the other thread probably has exited",
|
"broken IPC communication channel: the other thread probably has exited",
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
ElectrumClientError::CouldNotCreateConnection {
|
ElectrumError::CouldNotCreateConnection {
|
||||||
error_message: "message".to_string(),
|
error_message: "message".to_string(),
|
||||||
},
|
},
|
||||||
"message",
|
"message",
|
||||||
|
@ -25,7 +25,7 @@ use crate::error::CannotConnectError;
|
|||||||
use crate::error::CreateTxError;
|
use crate::error::CreateTxError;
|
||||||
use crate::error::DescriptorError;
|
use crate::error::DescriptorError;
|
||||||
use crate::error::DescriptorKeyError;
|
use crate::error::DescriptorKeyError;
|
||||||
use crate::error::ElectrumClientError;
|
use crate::error::ElectrumError;
|
||||||
use crate::error::EsploraError;
|
use crate::error::EsploraError;
|
||||||
use crate::error::ExtractTxError;
|
use crate::error::ExtractTxError;
|
||||||
use crate::error::FeeRateError;
|
use crate::error::FeeRateError;
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
import XCTest
|
||||||
|
@testable import BitcoinDevKit
|
||||||
|
|
||||||
|
private let SIGNET_ELECTRUM_URL = "ssl://mempool.space:60602"
|
||||||
|
|
||||||
|
final class LiveElectrumClientTests: XCTestCase {
|
||||||
|
func testSyncedBalance() throws {
|
||||||
|
let descriptor = try Descriptor(
|
||||||
|
descriptor: "wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)",
|
||||||
|
network: Network.signet
|
||||||
|
)
|
||||||
|
let wallet = try Wallet.newNoPersist(
|
||||||
|
descriptor: descriptor,
|
||||||
|
changeDescriptor: nil,
|
||||||
|
network: .signet
|
||||||
|
)
|
||||||
|
let electrumClient: ElectrumClient = try ElectrumClient(url: SIGNET_ELECTRUM_URL)
|
||||||
|
let fullScanRequest: FullScanRequest = wallet.startFullScan()
|
||||||
|
let update = try electrumClient.fullScan(
|
||||||
|
fullScanRequest: fullScanRequest,
|
||||||
|
stopGap: 10,
|
||||||
|
batchSize: 10,
|
||||||
|
fetchPrevTxouts: false
|
||||||
|
)
|
||||||
|
try wallet.applyUpdate(update: update)
|
||||||
|
let _ = try wallet.commit()
|
||||||
|
let address = try wallet.revealNextAddress(keychain: KeychainKind.external).address.asString()
|
||||||
|
|
||||||
|
XCTAssertGreaterThan(
|
||||||
|
wallet.getBalance().total.toSat(),
|
||||||
|
UInt64(0),
|
||||||
|
"Wallet must have positive balance, please send funds to \(address)"
|
||||||
|
)
|
||||||
|
|
||||||
|
print("Transactions count: \(wallet.transactions().count)")
|
||||||
|
let transactions = wallet.transactions().prefix(3)
|
||||||
|
for tx in transactions {
|
||||||
|
let sentAndReceived = wallet.sentAndReceived(tx: tx.transaction)
|
||||||
|
print("Transaction: \(tx.transaction.txid())")
|
||||||
|
print("Sent \(sentAndReceived.sent)")
|
||||||
|
print("Received \(sentAndReceived.received)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user