use std::collections::HashSet; #[allow(unused_imports)] use log::{debug, error, info, trace}; use bitcoin::{Script, Transaction, Txid}; use electrum_client::tokio::io::{AsyncRead, AsyncWrite}; use electrum_client::Client; use self::utils::{ELSGetHistoryRes, ELSListUnspentRes, ElectrumLikeSync}; use super::*; use crate::database::{BatchDatabase, DatabaseUtils}; use crate::error::Error; pub struct ElectrumBlockchain(Option>); impl std::convert::From> for ElectrumBlockchain { fn from(client: Client) -> Self { ElectrumBlockchain(Some(client)) } } impl Blockchain for ElectrumBlockchain { fn offline() -> Self { ElectrumBlockchain(None) } fn is_online(&self) -> bool { self.0.is_some() } } #[async_trait(?Send)] impl OnlineBlockchain for ElectrumBlockchain { async fn get_capabilities(&self) -> HashSet { vec![Capability::FullHistory, Capability::GetAnyTx] .into_iter() .collect() } async fn setup( &mut self, stop_gap: Option, database: &mut D, progress_update: P, ) -> Result<(), Error> { self.0 .as_mut() .ok_or(Error::OfflineClient)? .electrum_like_setup(stop_gap, database, progress_update) .await } async fn get_tx(&mut self, txid: &Txid) -> Result, Error> { Ok(self .0 .as_mut() .ok_or(Error::OfflineClient)? .transaction_get(txid) .await .map(Option::Some)?) } async fn broadcast(&mut self, tx: &Transaction) -> Result<(), Error> { Ok(self .0 .as_mut() .ok_or(Error::OfflineClient)? .transaction_broadcast(tx) .await .map(|_| ())?) } async fn get_height(&mut self) -> Result { // TODO: unsubscribe when added to the client, or is there a better call to use here? Ok(self .0 .as_mut() .ok_or(Error::OfflineClient)? .block_headers_subscribe() .await .map(|data| data.height)?) } } #[async_trait(?Send)] impl ElectrumLikeSync for Client { async fn els_batch_script_get_history<'s, I: IntoIterator>( &mut self, scripts: I, ) -> Result>, Error> { self.batch_script_get_history(scripts) .await .map(|v| { v.into_iter() .map(|v| { v.into_iter() .map( |electrum_client::GetHistoryRes { height, tx_hash, .. }| ELSGetHistoryRes { height, tx_hash, }, ) .collect() }) .collect() }) .map_err(Error::Electrum) } async fn els_batch_script_list_unspent<'s, I: IntoIterator>( &mut self, scripts: I, ) -> Result>, Error> { self.batch_script_list_unspent(scripts) .await .map(|v| { v.into_iter() .map(|v| { v.into_iter() .map( |electrum_client::ListUnspentRes { height, tx_hash, tx_pos, .. }| ELSListUnspentRes { height, tx_hash, tx_pos, }, ) .collect() }) .collect() }) .map_err(Error::Electrum) } async fn els_transaction_get(&mut self, txid: &Txid) -> Result { self.transaction_get(txid).await.map_err(Error::Electrum) } }