@@ -2,8 +2,11 @@ use bdk::bitcoin::{OutPoint, Transaction, TxOut, Txid};
22use bdk:: database:: BatchDatabase ;
33use bdk:: wallet:: Wallet ;
44
5+ #[ cfg( feature = "electrum" ) ]
6+ use electrum_client:: { Client as ElectrumClient , ElectrumApi } ;
7+
58#[ cfg( feature = "use-esplora-blocking" ) ]
6- use esplora_client:: BlockingClient ;
9+ use esplora_client:: BlockingClient as EsploraClient ;
710
811use std:: collections:: BTreeMap ;
912use std:: collections:: BTreeSet ;
@@ -196,15 +199,111 @@ where
196199 }
197200}
198201
202+ #[ cfg( feature = "electrum" ) ]
203+ pub struct ElectrumAtHeight < ' a > {
204+ client : & ' a ElectrumClient ,
205+ maximum_txout_height : Option < u32 > ,
206+ }
207+
208+ #[ cfg( feature = "electrum" ) ]
209+ impl TxOutSet for ElectrumClient {
210+ type Error = electrum_client:: Error ;
211+
212+ fn get_prevouts < ' a , I , T > ( & self , outpoints : I ) -> Result < T , Self :: Error >
213+ where
214+ I : IntoIterator < Item = & ' a OutPoint > ,
215+ T : FromIterator < Option < TxOut > > {
216+ let electrum_at_height = ElectrumAtHeight {
217+ client : self ,
218+ maximum_txout_height : None ,
219+ } ;
220+
221+ electrum_at_height. get_prevouts ( outpoints)
222+ }
223+ }
224+
225+ #[ cfg( feature = "electrum" ) ]
226+ impl < ' a > MaxHeightTxOutQuery < ' a > for ElectrumClient {
227+ type Target = ElectrumAtHeight < ' a > ;
228+
229+ fn txout_set_confirmed_by_height ( & ' a self , height : u32 ) -> Self :: Target {
230+ ElectrumAtHeight {
231+ client : self ,
232+ maximum_txout_height : Some ( height) ,
233+ }
234+ }
235+ }
236+
237+ #[ cfg( feature = "electrum" ) ]
238+ impl < ' a > TxOutSet for ElectrumAtHeight < ' a > {
239+ type Error = electrum_client:: Error ;
240+
241+ fn get_prevouts < ' b , I , T > ( & self , outpoints : I ) -> Result < T , Self :: Error >
242+ where
243+ I : IntoIterator < Item = & ' b OutPoint > ,
244+ T : FromIterator < Option < TxOut > > {
245+ let outpoints: Vec < _ > = outpoints. into_iter ( ) . collect ( ) ;
246+
247+ let input_txids: BTreeSet < Txid > = outpoints. iter ( ) . map ( |outpoint| outpoint. txid ) . collect ( ) ;
248+
249+ // avoiding the obvious batch_transaction_get optimization because
250+ // I'm not sure how it handles cases where some transactions are present but not others
251+ // FIXME: Probably should retain some types of errors here
252+ // and report them later
253+ let transactions: BTreeMap < & Txid , Transaction > = input_txids
254+ . iter ( )
255+ . filter_map ( |txid| {
256+ self . client . transaction_get ( txid)
257+ . map ( |tx| Some ( ( txid, tx) ) )
258+ . unwrap_or ( None )
259+ } )
260+ . collect ( ) ;
261+
262+ let iter = outpoints. iter ( )
263+ . map ( |outpoint| {
264+ let previous_tx = match transactions. get ( & outpoint. txid ) {
265+ Some ( previous_tx) => previous_tx,
266+ None => {
267+ return Ok ( None ) ;
268+ } ,
269+ } ;
270+
271+ let output = match previous_tx. output . get ( outpoint. vout as usize ) {
272+ Some ( output) => output,
273+ None => {
274+ return Ok ( None ) ;
275+ } ,
276+ } ;
277+
278+ let unspent = self . client . script_list_unspent ( & output. script_pubkey ) ?;
279+
280+ let output_in_unspent_list = unspent
281+ . iter ( )
282+ . find ( |unspent_info|
283+ unspent_info. tx_hash == outpoint. txid &&
284+ unspent_info. tx_pos == outpoint. vout as usize &&
285+ unspent_info. height <= ( self . maximum_txout_height . unwrap_or ( u32:: MAX ) as usize )
286+ ) ;
287+
288+ match output_in_unspent_list {
289+ Some ( _) => Ok ( Some ( output. to_owned ( ) ) ) ,
290+ None => Ok ( None ) ,
291+ }
292+ } ) ;
293+
294+ Result :: < T , Self :: Error > :: from_iter ( iter)
295+ }
296+ }
297+
199298#[ cfg( feature = "use-esplora-blocking" ) ]
200299pub struct EsploraAtHeight < ' a > {
201- client : & ' a BlockingClient ,
300+ client : & ' a EsploraClient ,
202301 height : Option < u32 > ,
203302}
204303
205304#[ cfg( feature = "use-esplora-blocking" ) ]
206305impl < ' a > EsploraAtHeight < ' a > {
207- pub fn new ( client : & ' a BlockingClient , height : Option < u32 > ) -> Self {
306+ pub fn new ( client : & ' a EsploraClient , height : Option < u32 > ) -> Self {
208307 Self { client, height }
209308 }
210309}
@@ -220,7 +319,7 @@ impl<'a> TxOutSet for EsploraAtHeight<'a> {
220319 {
221320 let outpoints: Vec < _ > = outpoints. into_iter ( ) . collect ( ) ;
222321
223- // Remove duplicate txids since the
322+ // Remove duplicate txids
224323 let input_txids: BTreeSet < Txid > = outpoints. iter ( ) . map ( |outpoint| outpoint. txid ) . collect ( ) ;
225324
226325 let transactions: BTreeMap < & Txid , Transaction > = input_txids
@@ -306,7 +405,7 @@ impl<'a> TxOutSet for EsploraAtHeight<'a> {
306405}
307406
308407#[ cfg( feature = "use-esplora-blocking" ) ]
309- impl < ' a > HistoricalTxOutQuery < ' a > for BlockingClient {
408+ impl < ' a > HistoricalTxOutQuery < ' a > for EsploraClient {
310409 type Target = EsploraAtHeight < ' a > ;
311410
312411 fn txout_set_at_height ( & ' a self , height : u32 ) -> Self :: Target {
@@ -318,7 +417,7 @@ impl<'a> HistoricalTxOutQuery<'a> for BlockingClient {
318417}
319418
320419#[ cfg( feature = "use-esplora-blocking" ) ]
321- impl < ' a > TxOutSet for BlockingClient {
420+ impl < ' a > TxOutSet for EsploraClient {
322421 type Error = esplora_client:: Error ;
323422
324423 fn get_prevouts < ' b , I , T > ( & self , outpoints : I ) -> Result < T , Self :: Error >
0 commit comments