Skip to content

Commit 8c93e0d

Browse files
committed
Merge #19: Add new API call get_block_status
5fbabb8 Add API call get_block_status (Gabriel Comte) Pull request description: ACKs for top commit: notmandatory: ACK 5fbabb8 Tree-SHA512: 40eebc336eec45a10b904daae4a221f630669602e52113d241b643fc7222dcf8171536bb98d2456916be8da3fb5a7bfa1f3fcb99a4702a403ee2ce1577402a3e
2 parents 4b00e46 + 5fbabb8 commit 8c93e0d

File tree

4 files changed

+79
-2
lines changed

4 files changed

+79
-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: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,51 @@ mod test {
460460
assert_eq!(block_header, block_header_async);
461461
}
462462

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

0 commit comments

Comments
 (0)