Skip to content

Commit 2753ac4

Browse files
authored
Merge pull request #410 from tnull/2024-11-iterative-mempool-retrieval
Poll mempool entries interatively
2 parents 34fd852 + 002c402 commit 2753ac4

File tree

2 files changed

+86
-43
lines changed

2 files changed

+86
-43
lines changed

src/chain/bitcoind_rpc.rs

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl BitcoindRpcClient {
5151
pub(crate) async fn broadcast_transaction(&self, tx: &Transaction) -> std::io::Result<Txid> {
5252
let tx_serialized = bitcoin::consensus::encode::serialize_hex(tx);
5353
let tx_json = serde_json::json!(tx_serialized);
54-
self.rpc_client.call_method::<Txid>("sendrawtransaction", &vec![tx_json]).await
54+
self.rpc_client.call_method::<Txid>("sendrawtransaction", &[tx_json]).await
5555
}
5656

5757
pub(crate) async fn get_fee_estimate_for_target(
@@ -62,15 +62,15 @@ impl BitcoindRpcClient {
6262
self.rpc_client
6363
.call_method::<FeeResponse>(
6464
"estimatesmartfee",
65-
&vec![num_blocks_json, estimation_mode_json],
65+
&[num_blocks_json, estimation_mode_json],
6666
)
6767
.await
6868
.map(|resp| resp.0)
6969
}
7070

7171
pub(crate) async fn get_mempool_minimum_fee_rate(&self) -> std::io::Result<FeeRate> {
7272
self.rpc_client
73-
.call_method::<MempoolMinFeeResponse>("getmempoolinfo", &vec![])
73+
.call_method::<MempoolMinFeeResponse>("getmempoolinfo", &[])
7474
.await
7575
.map(|resp| resp.0)
7676
}
@@ -82,7 +82,7 @@ impl BitcoindRpcClient {
8282
let txid_json = serde_json::json!(txid_hex);
8383
match self
8484
.rpc_client
85-
.call_method::<GetRawTransactionResponse>("getrawtransaction", &vec![txid_json])
85+
.call_method::<GetRawTransactionResponse>("getrawtransaction", &[txid_json])
8686
.await
8787
{
8888
Ok(resp) => Ok(Some(resp.0)),
@@ -113,14 +113,33 @@ impl BitcoindRpcClient {
113113
}
114114
}
115115

116-
pub(crate) async fn get_raw_mempool(&self) -> std::io::Result<Vec<RawMempoolEntry>> {
117-
let verbose_flag_json = serde_json::json!(true);
116+
pub(crate) async fn get_raw_mempool(&self) -> std::io::Result<Vec<Txid>> {
117+
let verbose_flag_json = serde_json::json!(false);
118118
self.rpc_client
119-
.call_method::<GetRawMempoolResponse>("getrawmempool", &vec![verbose_flag_json])
119+
.call_method::<GetRawMempoolResponse>("getrawmempool", &[verbose_flag_json])
120120
.await
121121
.map(|resp| resp.0)
122122
}
123123

124+
pub(crate) async fn get_mempool_entry(&self, txid: Txid) -> std::io::Result<MempoolEntry> {
125+
let txid_hex = bitcoin::consensus::encode::serialize_hex(&txid);
126+
let txid_json = serde_json::json!(txid_hex);
127+
self.rpc_client
128+
.call_method::<GetMempoolEntryResponse>("getmempoolentry", &[txid_json])
129+
.await
130+
.map(|resp| MempoolEntry { txid, height: resp.height, time: resp.time })
131+
}
132+
133+
pub(crate) async fn get_mempool_entries(&self) -> std::io::Result<Vec<MempoolEntry>> {
134+
let mempool_txids = self.get_raw_mempool().await?;
135+
let mut mempool_entries = Vec::with_capacity(mempool_txids.len());
136+
for txid in mempool_txids {
137+
let entry = self.get_mempool_entry(txid).await?;
138+
mempool_entries.push(entry);
139+
}
140+
Ok(mempool_entries)
141+
}
142+
124143
/// Get mempool transactions, alongside their first-seen unix timestamps.
125144
///
126145
/// This method is an adapted version of `bdk_bitcoind_rpc::Emitter::mempool`. It emits each
@@ -132,7 +151,7 @@ impl BitcoindRpcClient {
132151
let prev_mempool_time = self.latest_mempool_timestamp.load(Ordering::Relaxed);
133152
let mut latest_time = prev_mempool_time;
134153

135-
let mempool_entries = self.get_raw_mempool().await?;
154+
let mempool_entries = self.get_mempool_entries().await?;
136155
let mut txs_to_emit = Vec::new();
137156

138157
for entry in mempool_entries {
@@ -254,58 +273,82 @@ impl TryInto<GetRawTransactionResponse> for JsonResponse {
254273
}
255274
}
256275

257-
pub struct GetRawMempoolResponse(Vec<RawMempoolEntry>);
276+
pub struct GetRawMempoolResponse(Vec<Txid>);
258277

259278
impl TryInto<GetRawMempoolResponse> for JsonResponse {
260279
type Error = std::io::Error;
261280
fn try_into(self) -> std::io::Result<GetRawMempoolResponse> {
262-
let mut mempool_transactions = Vec::new();
263-
let res = self.0.as_object().ok_or(std::io::Error::new(
281+
let res = self.0.as_array().ok_or(std::io::Error::new(
264282
std::io::ErrorKind::Other,
265283
"Failed to parse getrawmempool response",
266284
))?;
267285

268-
for (k, v) in res {
269-
let txid = match bitcoin::consensus::encode::deserialize_hex(k) {
270-
Ok(txid) => txid,
271-
Err(_) => {
272-
return Err(std::io::Error::new(
273-
std::io::ErrorKind::Other,
274-
"Failed to parse getrawmempool response",
275-
));
276-
},
277-
};
278-
279-
let time = match v["time"].as_u64() {
280-
Some(time) => time,
281-
None => {
282-
return Err(std::io::Error::new(
283-
std::io::ErrorKind::Other,
284-
"Failed to parse getrawmempool response",
285-
));
286-
},
287-
};
286+
let mut mempool_transactions = Vec::with_capacity(res.len());
288287

289-
let height = match v["height"].as_u64().and_then(|h| h.try_into().ok()) {
290-
Some(height) => height,
291-
None => {
292-
return Err(std::io::Error::new(
293-
std::io::ErrorKind::Other,
294-
"Failed to parse getrawmempool response",
295-
));
296-
},
288+
for hex in res {
289+
let txid = if let Some(hex_str) = hex.as_str() {
290+
match bitcoin::consensus::encode::deserialize_hex(hex_str) {
291+
Ok(txid) => txid,
292+
Err(_) => {
293+
return Err(std::io::Error::new(
294+
std::io::ErrorKind::Other,
295+
"Failed to parse getrawmempool response",
296+
));
297+
},
298+
}
299+
} else {
300+
return Err(std::io::Error::new(
301+
std::io::ErrorKind::Other,
302+
"Failed to parse getrawmempool response",
303+
));
297304
};
298-
let entry = RawMempoolEntry { txid, time, height };
299305

300-
mempool_transactions.push(entry);
306+
mempool_transactions.push(txid);
301307
}
302308

303309
Ok(GetRawMempoolResponse(mempool_transactions))
304310
}
305311
}
306312

313+
pub struct GetMempoolEntryResponse {
314+
time: u64,
315+
height: u32,
316+
}
317+
318+
impl TryInto<GetMempoolEntryResponse> for JsonResponse {
319+
type Error = std::io::Error;
320+
fn try_into(self) -> std::io::Result<GetMempoolEntryResponse> {
321+
let res = self.0.as_object().ok_or(std::io::Error::new(
322+
std::io::ErrorKind::Other,
323+
"Failed to parse getmempoolentry response",
324+
))?;
325+
326+
let time = match res["time"].as_u64() {
327+
Some(time) => time,
328+
None => {
329+
return Err(std::io::Error::new(
330+
std::io::ErrorKind::Other,
331+
"Failed to parse getmempoolentry response",
332+
));
333+
},
334+
};
335+
336+
let height = match res["height"].as_u64().and_then(|h| h.try_into().ok()) {
337+
Some(height) => height,
338+
None => {
339+
return Err(std::io::Error::new(
340+
std::io::ErrorKind::Other,
341+
"Failed to parse getmempoolentry response",
342+
));
343+
},
344+
};
345+
346+
Ok(GetMempoolEntryResponse { time, height })
347+
}
348+
}
349+
307350
#[derive(Debug, Clone)]
308-
pub(crate) struct RawMempoolEntry {
351+
pub(crate) struct MempoolEntry {
309352
/// The transaction id
310353
txid: Txid,
311354
/// Local time transaction entered pool in seconds since 1 Jan 1970 GMT

src/chain/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub(crate) const DEFAULT_ESPLORA_SERVER_URL: &str = "https://blockstream.info/ap
5151
// The default Esplora client timeout we're using.
5252
pub(crate) const DEFAULT_ESPLORA_CLIENT_TIMEOUT_SECS: u64 = 10;
5353

54-
const CHAIN_POLLING_INTERVAL_SECS: u64 = 1;
54+
const CHAIN_POLLING_INTERVAL_SECS: u64 = 2;
5555

5656
pub(crate) enum WalletSyncStatus {
5757
Completed,

0 commit comments

Comments
 (0)