Skip to content

Commit d26dfd7

Browse files
committed
Merge #100: Implement batch call for script subscribe
40cecd5 Use Borrow for all items in IntoIterator (Riccardo Casatta) bf2f8ef script_subscribe IntoIterator accepts Borrow<&Script> (Riccardo Casatta) 156e6fc Implement batch call for script subscribe (Riccardo Casatta) Pull request description: script_subscribe call hasn't the batch counterpart which may be convenient for multiple subsequent subscribe. ACKs for top commit: afilini: ACK 40cecd5 Tree-SHA512: 26a3ebb9d1267a224c85b765cf793523771bf7e74990dd804a35693bfebc549f7524214725092eac2276f22cd24eafc72270986549ac02f52641db83fcb71324
2 parents 84d6860 + 40cecd5 commit d26dfd7

File tree

4 files changed

+101
-24
lines changed

4 files changed

+101
-24
lines changed

src/api.rs

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Electrum APIs
22
3+
use std::borrow::Borrow;
34
use std::convert::TryInto;
45

56
use bitcoin::consensus::encode::{deserialize, serialize};
@@ -38,7 +39,8 @@ pub trait ElectrumApi {
3839
/// Takes a list of `txids` and returns a list of transactions.
3940
fn batch_transaction_get<'t, I>(&self, txids: I) -> Result<Vec<Transaction>, Error>
4041
where
41-
I: IntoIterator<Item = &'t Txid> + Clone,
42+
I: IntoIterator + Clone,
43+
I::Item: Borrow<&'t Txid>,
4244
{
4345
self.batch_transaction_get_raw(txids)?
4446
.iter()
@@ -51,7 +53,8 @@ pub trait ElectrumApi {
5153
/// Takes a list of `heights` of blocks and returns a list of headers.
5254
fn batch_block_header<I>(&self, heights: I) -> Result<Vec<BlockHeader>, Error>
5355
where
54-
I: IntoIterator<Item = u32> + Clone,
56+
I: IntoIterator + Clone,
57+
I::Item: Borrow<u32>,
5558
{
5659
self.batch_block_header_raw(heights)?
5760
.iter()
@@ -106,6 +109,16 @@ pub trait ElectrumApi {
106109
/// already subscribed to the script.
107110
fn script_subscribe(&self, script: &Script) -> Result<Option<ScriptStatus>, Error>;
108111

112+
/// Batch version of [`script_subscribe`](#method.script_subscribe).
113+
///
114+
/// Takes a list of scripts and returns a list of script status responses.
115+
///
116+
/// Note you should pass a reference to a collection because otherwise an expensive clone is made
117+
fn batch_script_subscribe<'s, I>(&self, scripts: I) -> Result<Vec<Option<ScriptStatus>>, Error>
118+
where
119+
I: IntoIterator + Clone,
120+
I::Item: Borrow<&'s Script>;
121+
109122
/// Subscribes to notifications for activity on a specific *scriptPubKey*.
110123
///
111124
/// Returns a `bool` with the server response when successful.
@@ -125,7 +138,8 @@ pub trait ElectrumApi {
125138
/// Takes a list of scripts and returns a list of balance responses.
126139
fn batch_script_get_balance<'s, I>(&self, scripts: I) -> Result<Vec<GetBalanceRes>, Error>
127140
where
128-
I: IntoIterator<Item = &'s Script> + Clone;
141+
I: IntoIterator + Clone,
142+
I::Item: Borrow<&'s Script>;
129143

130144
/// Returns the history for a *scriptPubKey*
131145
fn script_get_history(&self, script: &Script) -> Result<Vec<GetHistoryRes>, Error>;
@@ -135,7 +149,8 @@ pub trait ElectrumApi {
135149
/// Takes a list of scripts and returns a list of history responses.
136150
fn batch_script_get_history<'s, I>(&self, scripts: I) -> Result<Vec<Vec<GetHistoryRes>>, Error>
137151
where
138-
I: IntoIterator<Item = &'s Script> + Clone;
152+
I: IntoIterator + Clone,
153+
I::Item: Borrow<&'s Script>;
139154

140155
/// Returns the list of unspent outputs for a *scriptPubKey*
141156
fn script_list_unspent(&self, script: &Script) -> Result<Vec<ListUnspentRes>, Error>;
@@ -148,7 +163,8 @@ pub trait ElectrumApi {
148163
scripts: I,
149164
) -> Result<Vec<Vec<ListUnspentRes>>, Error>
150165
where
151-
I: IntoIterator<Item = &'s Script> + Clone;
166+
I: IntoIterator + Clone,
167+
I::Item: Borrow<&'s Script>;
152168

153169
/// Gets the raw bytes of a transaction with `txid`. Returns an error if not found.
154170
fn transaction_get_raw(&self, txid: &Txid) -> Result<Vec<u8>, Error>;
@@ -158,22 +174,25 @@ pub trait ElectrumApi {
158174
/// Takes a list of `txids` and returns a list of transactions raw bytes.
159175
fn batch_transaction_get_raw<'t, I>(&self, txids: I) -> Result<Vec<Vec<u8>>, Error>
160176
where
161-
I: IntoIterator<Item = &'t Txid> + Clone;
177+
I: IntoIterator + Clone,
178+
I::Item: Borrow<&'t Txid>;
162179

163180
/// Batch version of [`block_header_raw`](#method.block_header_raw).
164181
///
165182
/// Takes a list of `heights` of blocks and returns a list of block header raw bytes.
166183
fn batch_block_header_raw<I>(&self, heights: I) -> Result<Vec<Vec<u8>>, Error>
167184
where
168-
I: IntoIterator<Item = u32> + Clone;
185+
I: IntoIterator + Clone,
186+
I::Item: Borrow<u32>;
169187

170188
/// Batch version of [`estimate_fee`](#method.estimate_fee).
171189
///
172190
/// Takes a list of `numbers` of blocks and returns a list of fee required in
173191
/// **Satoshis per kilobyte** to confirm a transaction in the given number of blocks.
174192
fn batch_estimate_fee<I>(&self, numbers: I) -> Result<Vec<f64>, Error>
175193
where
176-
I: IntoIterator<Item = usize> + Clone;
194+
I: IntoIterator + Clone,
195+
I::Item: Borrow<usize>;
177196

178197
/// Broadcasts the raw bytes of a transaction to the network.
179198
fn transaction_broadcast_raw(&self, raw_tx: &[u8]) -> Result<Txid, Error>;

src/batch.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
//!
33
//! This module contains definitions and helper functions used when making batch calls.
44
5+
use std::borrow::Borrow;
6+
57
use bitcoin::hashes::hex::ToHex;
68
use bitcoin::{Script, Txid};
79

@@ -43,6 +45,13 @@ impl Batch {
4345
.push((String::from("blockchain.scripthash.get_balance"), params));
4446
}
4547

48+
/// Add one `blockchain.scripthash.listunspent` request to the batch queue
49+
pub fn script_subscribe(&mut self, script: &Script) {
50+
let params = vec![Param::String(script.to_electrum_scripthash().to_hex())];
51+
self.calls
52+
.push((String::from("blockchain.scripthash.subscribe"), params));
53+
}
54+
4655
/// Add one `blockchain.transaction.get` request to the batch queue
4756
pub fn transaction_get(&mut self, tx_hash: &Txid) {
4857
let params = vec![Param::String(tx_hash.to_hex())];

src/client.rs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! Electrum Client
22
3-
use std::sync::RwLock;
3+
use std::{borrow::Borrow, sync::RwLock};
44

55
use log::{info, warn};
66

@@ -222,6 +222,15 @@ impl ElectrumApi for Client {
222222
impl_inner_call!(self, script_subscribe, script)
223223
}
224224

225+
#[inline]
226+
fn batch_script_subscribe<'s, I>(&self, scripts: I) -> Result<Vec<Option<ScriptStatus>>, Error>
227+
where
228+
I: IntoIterator + Clone,
229+
I::Item: Borrow<&'s Script>,
230+
{
231+
impl_inner_call!(self, batch_script_subscribe, scripts.clone())
232+
}
233+
225234
#[inline]
226235
fn script_unsubscribe(&self, script: &Script) -> Result<bool, Error> {
227236
impl_inner_call!(self, script_unsubscribe, script)
@@ -240,7 +249,8 @@ impl ElectrumApi for Client {
240249
#[inline]
241250
fn batch_script_get_balance<'s, I>(&self, scripts: I) -> Result<Vec<GetBalanceRes>, Error>
242251
where
243-
I: IntoIterator<Item = &'s Script> + Clone,
252+
I: IntoIterator + Clone,
253+
I::Item: Borrow<&'s Script>,
244254
{
245255
impl_inner_call!(self, batch_script_get_balance, scripts.clone())
246256
}
@@ -253,7 +263,8 @@ impl ElectrumApi for Client {
253263
#[inline]
254264
fn batch_script_get_history<'s, I>(&self, scripts: I) -> Result<Vec<Vec<GetHistoryRes>>, Error>
255265
where
256-
I: IntoIterator<Item = &'s Script> + Clone,
266+
I: IntoIterator + Clone,
267+
I::Item: Borrow<&'s Script>,
257268
{
258269
impl_inner_call!(self, batch_script_get_history, scripts.clone())
259270
}
@@ -269,7 +280,8 @@ impl ElectrumApi for Client {
269280
scripts: I,
270281
) -> Result<Vec<Vec<ListUnspentRes>>, Error>
271282
where
272-
I: IntoIterator<Item = &'s Script> + Clone,
283+
I: IntoIterator + Clone,
284+
I::Item: Borrow<&'s Script>,
273285
{
274286
impl_inner_call!(self, batch_script_list_unspent, scripts.clone())
275287
}
@@ -282,23 +294,26 @@ impl ElectrumApi for Client {
282294
#[inline]
283295
fn batch_transaction_get_raw<'t, I>(&self, txids: I) -> Result<Vec<Vec<u8>>, Error>
284296
where
285-
I: IntoIterator<Item = &'t Txid> + Clone,
297+
I: IntoIterator + Clone,
298+
I::Item: Borrow<&'t Txid>,
286299
{
287300
impl_inner_call!(self, batch_transaction_get_raw, txids.clone())
288301
}
289302

290303
#[inline]
291304
fn batch_block_header_raw<'s, I>(&self, heights: I) -> Result<Vec<Vec<u8>>, Error>
292305
where
293-
I: IntoIterator<Item = u32> + Clone,
306+
I: IntoIterator + Clone,
307+
I::Item: Borrow<u32>,
294308
{
295309
impl_inner_call!(self, batch_block_header_raw, heights.clone())
296310
}
297311

298312
#[inline]
299313
fn batch_estimate_fee<'s, I>(&self, numbers: I) -> Result<Vec<f64>, Error>
300314
where
301-
I: IntoIterator<Item = usize> + Clone,
315+
I: IntoIterator + Clone,
316+
I::Item: Borrow<usize>,
302317
{
303318
impl_inner_call!(self, batch_estimate_fee, numbers.clone())
304319
}

src/raw_client.rs

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
//!
33
//! This module contains the definition of the raw client that wraps the transport method
44
5+
use std::borrow::Borrow;
56
use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
67
use std::io::{BufRead, BufReader, Read, Write};
78
use std::mem::drop;
@@ -39,9 +40,17 @@ use types::*;
3940

4041
macro_rules! impl_batch_call {
4142
( $self:expr, $data:expr, $call:ident ) => {{
43+
impl_batch_call!($self, $data, $call, )
44+
}};
45+
46+
( $self:expr, $data:expr, $call:ident, apply_deref ) => {{
47+
impl_batch_call!($self, $data, $call, *)
48+
}};
49+
50+
( $self:expr, $data:expr, $call:ident, $($apply_deref:tt)? ) => {{
4251
let mut batch = Batch::default();
4352
for i in $data {
44-
batch.$call(i);
53+
batch.$call($($apply_deref)* i.borrow());
4554
}
4655

4756
let resp = $self.batch_call(&batch)?;
@@ -848,6 +857,25 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
848857
Ok(serde_json::from_value(value)?)
849858
}
850859

860+
fn batch_script_subscribe<'s, I>(&self, scripts: I) -> Result<Vec<Option<ScriptStatus>>, Error>
861+
where
862+
I: IntoIterator + Clone,
863+
I::Item: Borrow<&'s Script>,
864+
{
865+
{
866+
let mut script_notifications = self.script_notifications.lock()?;
867+
868+
for script in scripts.clone() {
869+
let script_hash = script.borrow().to_electrum_scripthash();
870+
if script_notifications.contains_key(&script_hash) {
871+
return Err(Error::AlreadySubscribed(script_hash));
872+
}
873+
script_notifications.insert(script_hash, VecDeque::new());
874+
}
875+
}
876+
impl_batch_call!(self, scripts, script_subscribe)
877+
}
878+
851879
fn script_unsubscribe(&self, script: &Script) -> Result<bool, Error> {
852880
let script_hash = script.to_electrum_scripthash();
853881
let mut script_notifications = self.script_notifications.lock()?;
@@ -891,7 +919,8 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
891919
}
892920
fn batch_script_get_balance<'s, I>(&self, scripts: I) -> Result<Vec<GetBalanceRes>, Error>
893921
where
894-
I: IntoIterator<Item = &'s Script> + Clone,
922+
I: IntoIterator + Clone,
923+
I::Item: Borrow<&'s Script>,
895924
{
896925
impl_batch_call!(self, scripts, script_get_balance)
897926
}
@@ -909,7 +938,8 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
909938
}
910939
fn batch_script_get_history<'s, I>(&self, scripts: I) -> Result<Vec<Vec<GetHistoryRes>>, Error>
911940
where
912-
I: IntoIterator<Item = &'s Script> + Clone,
941+
I: IntoIterator + Clone,
942+
I::Item: Borrow<&'s Script>,
913943
{
914944
impl_batch_call!(self, scripts, script_get_history)
915945
}
@@ -937,7 +967,8 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
937967
scripts: I,
938968
) -> Result<Vec<Vec<ListUnspentRes>>, Error>
939969
where
940-
I: IntoIterator<Item = &'s Script> + Clone,
970+
I: IntoIterator + Clone,
971+
I::Item: Borrow<&'s Script>,
941972
{
942973
impl_batch_call!(self, scripts, script_list_unspent)
943974
}
@@ -960,7 +991,8 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
960991

961992
fn batch_transaction_get_raw<'t, I>(&self, txids: I) -> Result<Vec<Vec<u8>>, Error>
962993
where
963-
I: IntoIterator<Item = &'t Txid> + Clone,
994+
I: IntoIterator + Clone,
995+
I::Item: Borrow<&'t Txid>,
964996
{
965997
let txs_string: Result<Vec<String>, Error> = impl_batch_call!(self, txids, transaction_get);
966998
txs_string?
@@ -971,10 +1003,11 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
9711003

9721004
fn batch_block_header_raw<'s, I>(&self, heights: I) -> Result<Vec<Vec<u8>>, Error>
9731005
where
974-
I: IntoIterator<Item = u32> + Clone,
1006+
I: IntoIterator + Clone,
1007+
I::Item: Borrow<u32>,
9751008
{
9761009
let headers_string: Result<Vec<String>, Error> =
977-
impl_batch_call!(self, heights, block_header);
1010+
impl_batch_call!(self, heights, block_header, apply_deref);
9781011
headers_string?
9791012
.iter()
9801013
.map(|s| Ok(Vec::<u8>::from_hex(s)?))
@@ -983,9 +1016,10 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
9831016

9841017
fn batch_estimate_fee<'s, I>(&self, numbers: I) -> Result<Vec<f64>, Error>
9851018
where
986-
I: IntoIterator<Item = usize> + Clone,
1019+
I: IntoIterator + Clone,
1020+
I::Item: Borrow<usize>,
9871021
{
988-
impl_batch_call!(self, numbers, estimate_fee)
1022+
impl_batch_call!(self, numbers, estimate_fee, apply_deref)
9891023
}
9901024

9911025
fn transaction_broadcast_raw(&self, raw_tx: &[u8]) -> Result<Txid, Error> {

0 commit comments

Comments
 (0)