Skip to content

Commit a414514

Browse files
committed
feat: add get_block_txs
1 parent b9dadcb commit a414514

File tree

3 files changed

+105
-0
lines changed

3 files changed

+105
-0
lines changed

src/async.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,29 @@ impl<S: Sleeper> AsyncClient<S> {
479479
self.get_response_json(&path).await
480480
}
481481

482+
/// Get up to 25 transactios from a [`Block`], given it's [`BlockHash`],
483+
/// beginning at `start_index` (starts from 0 if `start_index` is `None`).
484+
pub async fn get_block_txs(
485+
&self,
486+
blockhash: BlockHash,
487+
start_index: Option<u32>,
488+
) -> Result<Vec<Transaction>, Error> {
489+
let path = match start_index {
490+
None => format!("/block/{blockhash}/txs"),
491+
Some(idx) => format!("/block/{blockhash}/txs/{idx}"),
492+
};
493+
494+
let esplora_txs: Vec<Tx> = self.get_response_json(&path).await?;
495+
496+
// Convert Esplora [`Tx`]s into [`Transaction`]s.
497+
let txs: Vec<Transaction> = esplora_txs
498+
.into_iter()
499+
.map(|esplora_tx| esplora_tx.to_tx())
500+
.collect();
501+
502+
Ok(txs)
503+
}
504+
482505
/// Gets some recent block summaries starting at the tip or at `height` if
483506
/// provided.
484507
///

src/blocking.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,29 @@ impl BlockingClient {
404404
self.get_response_json(&path)
405405
}
406406

407+
/// Get up to 25 transactios from a [`Block`], given it's [`BlockHash`],
408+
/// beginning at `start_index` (starts from 0 if `start_index` is `None`).
409+
pub fn get_block_txs(
410+
&self,
411+
blockhash: BlockHash,
412+
start_index: Option<u32>,
413+
) -> Result<Vec<Transaction>, Error> {
414+
let path = match start_index {
415+
None => format!("/block/{blockhash}/txs"),
416+
Some(idx) => format!("/block/{blockhash}/txs/{idx}"),
417+
};
418+
419+
let esplora_txs: Vec<Tx> = self.get_response_json(&path)?;
420+
421+
// Convert Esplora [`Tx`]s into [`Transaction`]s.
422+
let txs: Vec<Transaction> = esplora_txs
423+
.into_iter()
424+
.map(|esplora_tx| esplora_tx.to_tx())
425+
.collect();
426+
427+
Ok(txs)
428+
}
429+
407430
/// Gets some recent block summaries starting at the tip or at `height` if
408431
/// provided.
409432
///

src/lib.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ mod test {
266266
use super::*;
267267
use electrsd::{corepc_node, ElectrsD};
268268
use lazy_static::lazy_static;
269+
use std::collections::HashSet;
269270
use std::env;
270271
use std::str::FromStr;
271272
use tokio::sync::Mutex;
@@ -1028,6 +1029,64 @@ mod test {
10281029
});
10291030
}
10301031

1032+
#[cfg(all(feature = "blocking", feature = "async"))]
1033+
#[tokio::test]
1034+
async fn test_get_block_txs() {
1035+
let (blocking_client, async_client) = setup_clients().await;
1036+
let address = BITCOIND
1037+
.client
1038+
.new_address_with_type(AddressType::Legacy)
1039+
.unwrap();
1040+
// Create 30 transactions and mine a block.
1041+
let mut mined_txids = Vec::new();
1042+
for _ in 0..30 {
1043+
let txid = BITCOIND
1044+
.client
1045+
.send_to_address(&address, Amount::from_sat(1000))
1046+
.unwrap()
1047+
.txid()
1048+
.unwrap();
1049+
mined_txids.push(txid);
1050+
}
1051+
let _miner = MINER.lock().await;
1052+
generate_blocks_and_wait(1);
1053+
tokio::time::sleep(tokio::time::Duration::from_secs(5)).await;
1054+
// Get the chain tip's blockhash.
1055+
let blockhash = blocking_client.get_tip_hash().unwrap();
1056+
let txs_blocking = blocking_client.get_block_txs(blockhash, None).unwrap();
1057+
let txs_async = async_client.get_block_txs(blockhash, None).await.unwrap();
1058+
// Assert that we only get 25 transactions, as per the Esplora specification.
1059+
assert_eq!(txs_blocking.len(), 25);
1060+
assert_eq!(txs_blocking, txs_async);
1061+
1062+
// Compare the received transactions (skipping the coinbase transaction).
1063+
// All 24 non-coinbase transactions should be from our expected set.
1064+
let expected_txids: HashSet<Txid> = mined_txids.iter().copied().collect();
1065+
let received_txids: HashSet<Txid> = txs_blocking
1066+
.iter()
1067+
.skip(1)
1068+
.map(|tx| tx.compute_txid())
1069+
.collect();
1070+
assert_eq!(received_txids.len(), 24);
1071+
assert!(received_txids.is_subset(&expected_txids));
1072+
1073+
let txs_blocking_offset = blocking_client.get_block_txs(blockhash, Some(25)).unwrap();
1074+
let txs_async_offset = async_client
1075+
.get_block_txs(blockhash, Some(25))
1076+
.await
1077+
.unwrap();
1078+
// 31 transactions on the block minus `start_index` of 25 yields 6 transactions.
1079+
assert_eq!(txs_blocking_offset.len(), 6);
1080+
assert_eq!(txs_blocking_offset, txs_async_offset);
1081+
1082+
// Compare the expected and received transactions from index 25 through 30.
1083+
let received_offset_txids: HashSet<Txid> = txs_blocking_offset
1084+
.iter()
1085+
.map(|tx| tx.compute_txid())
1086+
.collect();
1087+
assert!(received_offset_txids.is_subset(&expected_txids));
1088+
}
1089+
10311090
#[cfg(all(feature = "blocking", feature = "async"))]
10321091
#[tokio::test]
10331092
async fn test_get_blocks() {

0 commit comments

Comments
 (0)