Skip to content

Commit 6a9f76f

Browse files
committed
Add API call get_block_status
1 parent 4b00e46 commit 6a9f76f

File tree

4 files changed

+82
-2
lines changed

4 files changed

+82
-2
lines changed

src/api.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ pub struct OutputStatus {
5555
pub status: Option<TxStatus>,
5656
}
5757

58+
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
59+
pub struct BlockStatus {
60+
pub in_best_chain: bool,
61+
pub height: Option<u32>,
62+
pub next_best: Option<BlockHash>,
63+
}
64+
5865
#[derive(Deserialize, Clone, Debug)]
5966
pub struct Tx {
6067
pub txid: Txid,

src/async.rs

Lines changed: 12 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::{Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus};
27+
use crate::{BlockStatus, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus};
2828

2929
#[derive(Debug)]
3030
pub struct AsyncClient {
@@ -136,6 +136,17 @@ impl AsyncClient {
136136
Ok(header)
137137
}
138138

139+
/// Get the [`BlockStatus`] given a particular [`BlockHash`].
140+
pub async fn get_block_status(&self, block_hash: &BlockHash) -> Result<BlockStatus, Error> {
141+
let resp = self
142+
.client
143+
.get(&format!("{}/block/{}/status", self.url, block_hash))
144+
.send()
145+
.await?;
146+
147+
Ok(resp.error_for_status()?.json().await?)
148+
}
149+
139150
/// Get a merkle inclusion proof for a [`Transaction`] with the given [`Txid`].
140151
pub async fn get_merkle_proof(&self, tx_hash: &Txid) -> Result<Option<MerkleProof>, Error> {
141152
let resp = self

src/blocking.rs

Lines changed: 15 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::{BlockHash, BlockHeader, Script, Transaction, Txid};
2929

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

3232
#[derive(Debug, Clone)]
3333
pub struct BlockingClient {
@@ -150,6 +150,20 @@ impl BlockingClient {
150150
}
151151
}
152152

153+
/// Get the [`BlockStatus`] given a particular [`BlockHash`].
154+
pub fn get_block_status(&self, block_hash: &BlockHash) -> Result<BlockStatus, Error> {
155+
let resp = self
156+
.agent
157+
.get(&format!("{}/block/{}/status", self.url, block_hash))
158+
.call();
159+
160+
match resp {
161+
Ok(resp) => Ok(resp.into_json()?),
162+
Err(ureq::Error::Status(code, _)) => Err(Error::HttpResponse(code)),
163+
Err(e) => Err(Error::Ureq(e)),
164+
}
165+
}
166+
153167
/// Get a merkle inclusion proof for a [`Transaction`] with the given [`Txid`].
154168
pub fn get_merkle_proof(&self, txid: &Txid) -> Result<Option<MerkleProof>, Error> {
155169
let resp = self

src/lib.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ mod test {
201201
use electrsd::{bitcoind, bitcoind::BitcoinD, ElectrsD};
202202
use lazy_static::lazy_static;
203203
use std::env;
204+
use std::str::FromStr;
204205
use tokio::sync::Mutex;
205206
#[cfg(all(feature = "blocking", any(feature = "async", feature = "async-https")))]
206207
use {
@@ -460,6 +461,53 @@ mod test {
460461
assert_eq!(block_header, block_header_async);
461462
}
462463

464+
#[cfg(all(feature = "blocking", any(feature = "async", feature = "async-https")))]
465+
#[tokio::test]
466+
async fn test_get_block_status() {
467+
let (blocking_client, async_client) = setup_clients().await;
468+
469+
let block_hash = BITCOIND.client.get_block_hash(21).unwrap();
470+
let next_block_hash = BITCOIND.client.get_block_hash(22).unwrap();
471+
472+
let expected = BlockStatus {
473+
in_best_chain: true,
474+
height: Some(21),
475+
next_best: Some(next_block_hash),
476+
};
477+
478+
let block_status = blocking_client.get_block_status(&block_hash).unwrap();
479+
let block_status_async = async_client.get_block_status(&block_hash).await.unwrap();
480+
assert_eq!(expected, block_status);
481+
assert_eq!(expected, block_status_async);
482+
}
483+
484+
#[cfg(all(feature = "blocking", any(feature = "async", feature = "async-https")))]
485+
#[tokio::test]
486+
async fn test_get_non_existing_block_status() {
487+
// Esplora returns the same status for orphaned blocks as for non-existing blocks:
488+
// non-existing: https://blockstream.info/api/block/0000000000000000000000000000000000000000000000000000000000000000/status
489+
// orphaned: https://blockstream.info/api/block/000000000000000000181b1a2354620f66868a723c0c4d5b24e4be8bdfc35a7f/status
490+
// (Here the block is cited as orphaned: https://bitcoinchain.com/block_explorer/block/000000000000000000181b1a2354620f66868a723c0c4d5b24e4be8bdfc35a7f/ )
491+
// For this reason, we only test for the non-existing case here.
492+
493+
let (blocking_client, async_client) = setup_clients().await;
494+
495+
let block_hash =
496+
BlockHash::from_str("0000000000000000000000000000000000000000000000000000000000000000")
497+
.unwrap();
498+
499+
let expected = BlockStatus {
500+
in_best_chain: false,
501+
height: None,
502+
next_best: None,
503+
};
504+
505+
let block_status = blocking_client.get_block_status(&block_hash).unwrap();
506+
let block_status_async = async_client.get_block_status(&block_hash).await.unwrap();
507+
assert_eq!(expected, block_status);
508+
assert_eq!(expected, block_status_async);
509+
}
510+
463511
#[cfg(all(feature = "blocking", any(feature = "async", feature = "async-https")))]
464512
#[tokio::test]
465513
async fn test_get_merkle_proof() {

0 commit comments

Comments
 (0)