Skip to content

Commit 3814040

Browse files
committed
Add /blocks API
Not all the fields have been added yet.
1 parent 3030d2b commit 3814040

File tree

4 files changed

+75
-3
lines changed

4 files changed

+75
-3
lines changed

src/api.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,22 @@ pub struct Tx {
7373
pub fee: u64,
7474
}
7575

76-
#[derive(Deserialize, Clone, Debug)]
76+
#[derive(Deserialize, Clone, Debug, PartialEq)]
7777
pub struct BlockTime {
7878
pub timestamp: u64,
7979
pub height: u32,
8080
}
8181

82+
#[derive(Debug, Clone, Deserialize, PartialEq)]
83+
pub struct BlockSummary {
84+
pub id: BlockHash,
85+
#[serde(flatten)]
86+
pub time: BlockTime,
87+
/// Hash of the previous block, will be `None` for the genesis block.
88+
pub previousblockhash: Option<bitcoin::BlockHash>,
89+
pub merkle_root: bitcoin::TxMerkleNode,
90+
}
91+
8292
impl Tx {
8393
pub fn to_tx(&self) -> Transaction {
8494
Transaction {

src/async.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use log::{debug, error, info, trace};
2424

2525
use reqwest::{Client, StatusCode};
2626

27-
use crate::{BlockStatus, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus};
27+
use crate::{BlockStatus, BlockSummary, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus};
2828

2929
#[derive(Debug)]
3030
pub struct AsyncClient {
@@ -303,4 +303,24 @@ impl AsyncClient {
303303
.json::<HashMap<String, f64>>()
304304
.await?)
305305
}
306+
307+
/// Gets some recent block summaries starting at the tip or at `height` if provided.
308+
///
309+
/// The maximum number of summaries returned depends on the backend itself: esplora returns `10`
310+
/// while [mempool.space](https://mempool.space/docs/api) returns `15`.
311+
pub async fn get_blocks(&self, height: Option<u32>) -> Result<Vec<BlockSummary>, Error> {
312+
let url = match height {
313+
Some(height) => format!("{}/blocks/{}", self.url, height),
314+
None => format!("{}/blocks", self.url),
315+
};
316+
317+
Ok(self
318+
.client
319+
.get(&url)
320+
.send()
321+
.await?
322+
.error_for_status()?
323+
.json()
324+
.await?)
325+
}
306326
}

src/blocking.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use bitcoin::hashes::hex::{FromHex, ToHex};
2727
use bitcoin::hashes::{sha256, Hash};
2828
use bitcoin::{Block, BlockHash, BlockHeader, MerkleBlock, Script, Transaction, Txid};
2929

30-
use crate::{BlockStatus, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus};
30+
use crate::{BlockStatus, BlockSummary, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus};
3131

3232
#[derive(Debug, Clone)]
3333
pub struct BlockingClient {
@@ -344,6 +344,19 @@ impl BlockingClient {
344344
};
345345
Ok(self.agent.get(&url).call()?.into_json()?)
346346
}
347+
348+
/// Gets some recent block summaries starting at the tip or at `height` if provided.
349+
///
350+
/// The maximum number of summaries returned depends on the backend itself: esplora returns `10`
351+
/// while [mempool.space](https://mempool.space/docs/api) returns `15`.
352+
pub fn get_blocks(&self, height: Option<u32>) -> Result<Vec<BlockSummary>, Error> {
353+
let url = match height {
354+
Some(height) => format!("{}/blocks/{}", self.url, height),
355+
None => format!("{}/blocks", self.url),
356+
};
357+
358+
Ok(self.agent.get(&url).call()?.into_json()?)
359+
}
347360
}
348361

349362
fn is_status_not_found(status: u16) -> bool {

src/lib.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -756,4 +756,33 @@ mod test {
756756
.collect();
757757
assert_eq!(scripthash_txs_txids, scripthash_txs_txids_async);
758758
}
759+
760+
#[cfg(all(feature = "blocking", any(feature = "async", feature = "async-https")))]
761+
#[tokio::test]
762+
async fn test_get_blocks() {
763+
let (blocking_client, async_client) = setup_clients().await;
764+
let start_height = BITCOIND.client.get_block_count().unwrap();
765+
let blocks1 = blocking_client.get_blocks(None).unwrap();
766+
let blocks_async1 = async_client.get_blocks(None).await.unwrap();
767+
assert_eq!(blocks1[0].time.height, start_height as u32);
768+
assert_eq!(blocks1, blocks_async1);
769+
generate_blocks_and_wait(10);
770+
let blocks2 = blocking_client.get_blocks(None).unwrap();
771+
let blocks_async2 = async_client.get_blocks(None).await.unwrap();
772+
assert_eq!(blocks2, blocks_async2);
773+
assert_ne!(blocks2, blocks1);
774+
let blocks3 = blocking_client
775+
.get_blocks(Some(start_height as u32))
776+
.unwrap();
777+
let blocks_async3 = async_client
778+
.get_blocks(Some(start_height as u32))
779+
.await
780+
.unwrap();
781+
assert_eq!(blocks3, blocks_async3);
782+
assert_eq!(blocks3[0].time.height, start_height as u32);
783+
assert_eq!(blocks3, blocks1);
784+
let blocks_genesis = blocking_client.get_blocks(Some(0)).unwrap();
785+
let blocks_genesis_async = async_client.get_blocks(Some(0)).await.unwrap();
786+
assert_eq!(blocks_genesis, blocks_genesis_async);
787+
}
759788
}

0 commit comments

Comments
 (0)