feat: add simple electrum client
This commit is contained in:
parent
c88b33473b
commit
8d30c86076
56
bdk-ffi/Cargo.lock
generated
56
bdk-ffi/Cargo.lock
generated
@ -161,6 +161,7 @@ version = "1.0.0-alpha.10"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"bdk",
|
||||
"bdk_electrum",
|
||||
"bdk_esplora",
|
||||
"bdk_file_store",
|
||||
"bitcoin-internals",
|
||||
@ -179,6 +180,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bdk_electrum"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44bbf3b0031651a37a48bdfab0c1d96a305b587f616593d34df9b1ff63efc4ff"
|
||||
dependencies = [
|
||||
"bdk_chain",
|
||||
"electrum-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bdk_esplora"
|
||||
version = "0.13.0"
|
||||
@ -286,6 +297,12 @@ version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "bytes"
|
||||
version = "1.6.0"
|
||||
@ -382,6 +399,23 @@ version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||
|
||||
[[package]]
|
||||
name = "electrum-client"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89008f106be6f303695522f2f4c1f28b40c3e8367ed8b3bb227f1f882cb52cc2"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"byteorder",
|
||||
"libc",
|
||||
"log",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"webpki-roots",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esplora-client"
|
||||
version = "0.7.0"
|
||||
@ -1129,6 +1163,28 @@ dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
|
@ -20,6 +20,7 @@ default = ["uniffi/cli"]
|
||||
[dependencies]
|
||||
bdk = { version = "1.0.0-alpha.11", features = ["all-keys", "keys-bip39"] }
|
||||
bdk_esplora = { version = "0.13.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] }
|
||||
bdk_electrum = { version = "0.13.0" }
|
||||
bdk_file_store = { version = "0.11.0" }
|
||||
|
||||
uniffi = { version = "=0.26.1" }
|
||||
|
@ -101,6 +101,26 @@ interface DescriptorKeyError {
|
||||
Bip32(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface ElectrumClientError {
|
||||
IOError(string error_message);
|
||||
Json(string error_message);
|
||||
Hex(string error_message);
|
||||
Protocol(string error_message);
|
||||
Bitcoin(string error_message);
|
||||
AlreadySubscribed();
|
||||
NotSubscribed();
|
||||
InvalidResponse(string error_message);
|
||||
Message(string error_message);
|
||||
InvalidDNSNameError(string domain);
|
||||
MissingDomain();
|
||||
AllAttemptsErrored();
|
||||
SharedIOError(string error_message);
|
||||
CouldntLockReader();
|
||||
Mpsc();
|
||||
CouldNotCreateConnection(string error_message);
|
||||
};
|
||||
|
||||
[Error]
|
||||
interface EsploraError {
|
||||
Minreq(string error_message);
|
||||
@ -467,6 +487,15 @@ interface EsploraClient {
|
||||
void broadcast([ByRef] Transaction transaction);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk_electrum crate
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
interface ElectrumClient {
|
||||
[Throws=ElectrumClientError]
|
||||
constructor(string url);
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// bdk-ffi-defined types
|
||||
// ------------------------------------------------------------------------
|
||||
|
12
bdk-ffi/src/electrum.rs
Normal file
12
bdk-ffi/src/electrum.rs
Normal file
@ -0,0 +1,12 @@
|
||||
use crate::error::ElectrumClientError;
|
||||
|
||||
use bdk_electrum::electrum_client::Client as BdkBlockingClient;
|
||||
|
||||
pub struct ElectrumClient(BdkBlockingClient);
|
||||
|
||||
impl ElectrumClient {
|
||||
pub fn new(url: String) -> Result<Self, ElectrumClientError> {
|
||||
let client = BdkBlockingClient::new(url.as_str())?;
|
||||
Ok(Self(client))
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ use bdk::wallet::error::CreateTxError as BdkCreateTxError;
|
||||
use bdk::wallet::signer::SignerError as BdkSignerError;
|
||||
use bdk::wallet::tx_builder::AddUtxoError;
|
||||
use bdk::wallet::NewOrLoadError;
|
||||
use bdk_electrum::electrum_client::Error as BdkElectrumError;
|
||||
use bdk_esplora::esplora_client::{Error as BdkEsploraError, Error};
|
||||
use bdk_file_store::FileError as BdkFileError;
|
||||
use bitcoin_internals::hex::display::DisplayHex;
|
||||
@ -255,6 +256,57 @@ pub enum DescriptorKeyError {
|
||||
Bip32 { error_message: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ElectrumClientError {
|
||||
#[error("{error_message}")]
|
||||
IOError { error_message: String },
|
||||
|
||||
#[error("{error_message}")]
|
||||
Json { error_message: String },
|
||||
|
||||
#[error("{error_message}")]
|
||||
Hex { error_message: String },
|
||||
|
||||
#[error("electrum server error: {error_message}")]
|
||||
Protocol { error_message: String },
|
||||
|
||||
#[error("{error_message}")]
|
||||
Bitcoin { error_message: String },
|
||||
|
||||
#[error("already subscribed to the notifications of an address")]
|
||||
AlreadySubscribed,
|
||||
|
||||
#[error("not subscribed to the notifications of an address")]
|
||||
NotSubscribed,
|
||||
|
||||
#[error("error during the deserialization of a response from the server: {error_message}")]
|
||||
InvalidResponse { error_message: String },
|
||||
|
||||
#[error("{error_message}")]
|
||||
Message { error_message: String },
|
||||
|
||||
#[error("invalid domain name {domain} not matching SSL certificate")]
|
||||
InvalidDNSNameError { domain: String },
|
||||
|
||||
#[error("missing domain while it was explicitly asked to validate it")]
|
||||
MissingDomain,
|
||||
|
||||
#[error("made one or multiple attempts, all errored")]
|
||||
AllAttemptsErrored,
|
||||
|
||||
#[error("{error_message}")]
|
||||
SharedIOError { error_message: String },
|
||||
|
||||
#[error("couldn't take a lock on the reader mutex. This means that there's already another reader thread is running")]
|
||||
CouldntLockReader,
|
||||
|
||||
#[error("broken IPC communication channel: the other thread probably has exited")]
|
||||
Mpsc,
|
||||
|
||||
#[error("{error_message}")]
|
||||
CouldNotCreateConnection { error_message: String },
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum EsploraError {
|
||||
#[error("minreq error: {error_message}")]
|
||||
@ -503,6 +555,51 @@ impl From<BdkAddressError> for AddressError {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<BdkElectrumError> for ElectrumClientError {
|
||||
fn from(error: BdkElectrumError) -> Self {
|
||||
match error {
|
||||
BdkElectrumError::IOError(e) => ElectrumClientError::IOError {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkElectrumError::JSON(e) => ElectrumClientError::Json {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkElectrumError::Hex(e) => ElectrumClientError::Hex {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkElectrumError::Protocol(e) => ElectrumClientError::Protocol {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkElectrumError::Bitcoin(e) => ElectrumClientError::Bitcoin {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkElectrumError::AlreadySubscribed(_) => ElectrumClientError::AlreadySubscribed,
|
||||
BdkElectrumError::NotSubscribed(_) => ElectrumClientError::NotSubscribed,
|
||||
BdkElectrumError::InvalidResponse(e) => ElectrumClientError::InvalidResponse {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkElectrumError::Message(e) => ElectrumClientError::Message {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkElectrumError::InvalidDNSNameError(domain) => {
|
||||
ElectrumClientError::InvalidDNSNameError { domain }
|
||||
}
|
||||
BdkElectrumError::MissingDomain => ElectrumClientError::MissingDomain,
|
||||
BdkElectrumError::AllAttemptsErrored(_) => ElectrumClientError::AllAttemptsErrored,
|
||||
BdkElectrumError::SharedIOError(e) => ElectrumClientError::SharedIOError {
|
||||
error_message: e.to_string(),
|
||||
},
|
||||
BdkElectrumError::CouldntLockReader => ElectrumClientError::CouldntLockReader,
|
||||
BdkElectrumError::Mpsc => ElectrumClientError::Mpsc,
|
||||
BdkElectrumError::CouldNotCreateConnection(error_message) => {
|
||||
ElectrumClientError::CouldNotCreateConnection {
|
||||
error_message: error_message.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ParseError> for AddressError {
|
||||
fn from(error: ParseError) -> Self {
|
||||
match error {
|
||||
@ -977,11 +1074,7 @@ impl From<NewOrLoadError> for WalletCreationError {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::error::{
|
||||
AddressError, Bip32Error, Bip39Error, CannotConnectError, CreateTxError, DescriptorError,
|
||||
DescriptorKeyError, EsploraError, ExtractTxError, FeeRateError, ParseAmountError,
|
||||
PersistenceError, PsbtParseError, TransactionError, TxidParseError, WalletCreationError,
|
||||
};
|
||||
use crate::error::{AddressError, Bip32Error, Bip39Error, CannotConnectError, CreateTxError, DescriptorError, DescriptorKeyError, ElectrumClientError, EsploraError, ExtractTxError, FeeRateError, ParseAmountError, PersistenceError, PsbtParseError, TransactionError, TxidParseError, WalletCreationError};
|
||||
use crate::CalculateFeeError;
|
||||
use crate::OutPoint;
|
||||
use crate::SignerError;
|
||||
@ -1383,6 +1476,92 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_electrum_client() {
|
||||
let cases = vec![
|
||||
(
|
||||
ElectrumClientError::IOError { error_message: "message".to_string(), },
|
||||
"message",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::Json { error_message: "message".to_string(), },
|
||||
"message",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::Hex { error_message: "message".to_string(), },
|
||||
"message",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::Protocol { error_message: "message".to_string(), },
|
||||
"electrum server error: message",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::Bitcoin {
|
||||
error_message: "message".to_string(),
|
||||
},
|
||||
"message",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::AlreadySubscribed,
|
||||
"already subscribed to the notifications of an address",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::NotSubscribed,
|
||||
"not subscribed to the notifications of an address",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::InvalidResponse {
|
||||
error_message: "message".to_string(),
|
||||
},
|
||||
"error during the deserialization of a response from the server: message",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::Message {
|
||||
error_message: "message".to_string(),
|
||||
},
|
||||
"message",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::InvalidDNSNameError {
|
||||
domain: "domain".to_string(),
|
||||
},
|
||||
"invalid domain name domain not matching SSL certificate",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::MissingDomain,
|
||||
"missing domain while it was explicitly asked to validate it",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::AllAttemptsErrored,
|
||||
"made one or multiple attempts, all errored",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::SharedIOError {
|
||||
error_message: "message".to_string(),
|
||||
},
|
||||
"message",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::CouldntLockReader,
|
||||
"couldn't take a lock on the reader mutex. This means that there's already another reader thread is running"
|
||||
),
|
||||
(
|
||||
ElectrumClientError::Mpsc,
|
||||
"broken IPC communication channel: the other thread probably has exited",
|
||||
),
|
||||
(
|
||||
ElectrumClientError::CouldNotCreateConnection {
|
||||
error_message: "message".to_string(),
|
||||
},
|
||||
"message",
|
||||
)
|
||||
];
|
||||
|
||||
for (error, expected_message) in cases {
|
||||
assert_eq!(error.to_string(), expected_message);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_esplora() {
|
||||
let cases = vec![
|
||||
|
@ -1,5 +1,6 @@
|
||||
mod bitcoin;
|
||||
mod descriptor;
|
||||
mod electrum;
|
||||
mod error;
|
||||
mod esplora;
|
||||
mod keys;
|
||||
@ -15,6 +16,7 @@ use crate::bitcoin::Script;
|
||||
use crate::bitcoin::Transaction;
|
||||
use crate::bitcoin::TxOut;
|
||||
use crate::descriptor::Descriptor;
|
||||
use crate::electrum::ElectrumClient;
|
||||
use crate::error::AddressError;
|
||||
use crate::error::Bip32Error;
|
||||
use crate::error::Bip39Error;
|
||||
@ -23,6 +25,7 @@ use crate::error::CannotConnectError;
|
||||
use crate::error::CreateTxError;
|
||||
use crate::error::DescriptorError;
|
||||
use crate::error::DescriptorKeyError;
|
||||
use crate::error::ElectrumClientError;
|
||||
use crate::error::EsploraError;
|
||||
use crate::error::ExtractTxError;
|
||||
use crate::error::FeeRateError;
|
||||
|
Loading…
x
Reference in New Issue
Block a user