Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ use serde::Deserialize;
use std::collections::HashMap;

pub use bitcoin::consensus::{deserialize, serialize};
use bitcoin::hash_types::TxMerkleNode;
pub use bitcoin::hex::FromHex;
pub use bitcoin::{
absolute, block, transaction, Address, Amount, Block, BlockHash, CompactTarget, FeeRate,
Expand Down Expand Up @@ -204,20 +203,6 @@ pub struct BlockTime {
pub height: u32,
}

/// Summary about a [`Block`].
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct BlockSummary {
/// The [`Block`]'s hash.
pub id: BlockHash,
/// The [`Block`]'s timestamp and height.
#[serde(flatten)]
pub time: BlockTime,
/// The [`BlockHash`] of the previous [`Block`] (`None` for the genesis [`Block`]).
pub previousblockhash: Option<BlockHash>,
/// The Merkle root of the [`Block`]'s [`Transaction`]s.
pub merkle_root: TxMerkleNode,
}

/// Statistics about an [`Address`].
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct AddressStats {
Expand Down
42 changes: 22 additions & 20 deletions src/async.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ use log::{debug, error, info, trace};
use reqwest::{header, Body, Client, Response};

use crate::{
AddressStats, BlockInfo, BlockStatus, BlockSummary, Builder, Error, MempoolRecentTx,
MempoolStats, MerkleProof, OutputStatus, ScriptHashStats, SubmitPackageResult, Tx, TxStatus,
Utxo, BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
AddressStats, BlockInfo, BlockStatus, Builder, Error, MempoolRecentTx, MempoolStats,
MerkleProof, OutputStatus, ScriptHashStats, SubmitPackageResult, Tx, TxStatus, Utxo,
BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
};

/// An async client for interacting with an Esplora API server.
Expand Down Expand Up @@ -533,6 +533,25 @@ impl<S: Sleeper> AsyncClient<S> {
self.get_response_json(&path).await
}

/// Get [block summaries](BlockInfo) for recent blocks:
/// - If `height` is `None`: from the tip
/// - If `height is `Some(height)`: from `height`
///
/// The maximum number of [block summaries](BlockInfo) returned depends on the backend:
/// - Esplora returns 10
/// - [Mempool.space](https://mempool.space/docs/api/rest#get-blocks) returns 10
Comment thread
luisschwab marked this conversation as resolved.
Outdated
pub async fn get_block_infos(&self, height: Option<u32>) -> Result<Vec<BlockInfo>, Error> {
let path = match height {
Some(height) => format!("/blocks/{height}"),
None => "/blocks".to_string(),
};
let block_infos: Vec<BlockInfo> = self.get_response_json(&path).await?;
if block_infos.is_empty() {
return Err(Error::InvalidResponse);
}
Ok(block_infos)
}

/// Get all [`Txid`]s that belong to a [`Block`] identified by it's [`BlockHash`].
pub async fn get_block_txids(&self, blockhash: &BlockHash) -> Result<Vec<Txid>, Error> {
let path = format!("/block/{blockhash}/txids");
Expand All @@ -558,23 +577,6 @@ impl<S: Sleeper> AsyncClient<S> {
self.get_response_json(&path).await
}

/// Gets some recent block summaries starting at the tip or at `height` if
/// provided.
///
/// The maximum number of summaries returned depends on the backend itself:
/// esplora returns `10` while [mempool.space](https://mempool.space/docs/api) returns `15`.
pub async fn get_blocks(&self, height: Option<u32>) -> Result<Vec<BlockSummary>, Error> {
let path = match height {
Some(height) => format!("/blocks/{height}"),
None => "/blocks".to_string(),
};
let blocks: Vec<BlockSummary> = self.get_response_json(&path).await?;
if blocks.is_empty() {
return Err(Error::InvalidResponse);
}
Ok(blocks)
}

/// Get all UTXOs locked to an address.
pub async fn get_address_utxos(&self, address: &Address) -> Result<Vec<Utxo>, Error> {
let path = format!("/address/{address}/utxo");
Expand Down
42 changes: 22 additions & 20 deletions src/blocking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ use bitcoin::hex::{DisplayHex, FromHex};
use bitcoin::{Address, Block, BlockHash, MerkleBlock, Script, Transaction, Txid};

use crate::{
AddressStats, BlockInfo, BlockStatus, BlockSummary, Builder, Error, MempoolRecentTx,
MempoolStats, MerkleProof, OutputStatus, ScriptHashStats, SubmitPackageResult, Tx, TxStatus,
Utxo, BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
AddressStats, BlockInfo, BlockStatus, Builder, Error, MempoolRecentTx, MempoolStats,
MerkleProof, OutputStatus, ScriptHashStats, SubmitPackageResult, Tx, TxStatus, Utxo,
BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
};

/// A blocking client for interacting with an Esplora API server.
Expand Down Expand Up @@ -471,6 +471,25 @@ impl BlockingClient {
self.get_response_json(&path)
}

/// Get [block summaries](BlockInfo) for recent blocks:
/// - If `height` is `None`: from the tip
/// - If `height is `Some(height)`: from `height`
///
/// The maximum number of [block summaries](BlockInfo) returned depends on the backend:
/// - Esplora returns 10
/// - [Mempool.space](https://mempool.space/docs/api/rest#get-blocks) returns 10
pub fn get_block_infos(&self, height: Option<u32>) -> Result<Vec<BlockInfo>, Error> {
let path = match height {
Some(height) => format!("/blocks/{height}"),
None => "/blocks".to_string(),
};
let block_infos: Vec<BlockInfo> = self.get_response_json(&path)?;
if block_infos.is_empty() {
return Err(Error::InvalidResponse);
}
Ok(block_infos)
}

/// Get all [`Txid`]s that belong to a [`Block`] identified by it's [`BlockHash`].
pub fn get_block_txids(&self, blockhash: &BlockHash) -> Result<Vec<Txid>, Error> {
let path = format!("/block/{blockhash}/txids");
Expand All @@ -496,23 +515,6 @@ impl BlockingClient {
self.get_response_json(&path)
}

/// Gets some recent block summaries starting at the tip or at `height` if
/// provided.
///
/// The maximum number of summaries returned depends on the backend itself:
/// esplora returns `10` while [mempool.space](https://mempool.space/docs/api) returns `15`.
pub fn get_blocks(&self, height: Option<u32>) -> Result<Vec<BlockSummary>, Error> {
let path = match height {
Some(height) => format!("/blocks/{height}"),
None => "/blocks".to_string(),
};
let blocks: Vec<BlockSummary> = self.get_response_json(&path)?;
if blocks.is_empty() {
return Err(Error::InvalidResponse);
}
Ok(blocks)
}

/// Get all UTXOs locked to an address.
pub fn get_address_utxos(&self, address: &Address) -> Result<Vec<Utxo>, Error> {
let path = format!("/address/{address}/utxo");
Expand Down
22 changes: 11 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,35 +1063,35 @@ mod test {

#[cfg(all(feature = "blocking", feature = "async"))]
#[tokio::test]
async fn test_get_blocks() {
async fn test_get_block_infos() {
let env = TestEnv::new();
let (blocking_client, async_client) = env.setup_clients();

let start_height = env.bitcoind_client().get_block_count().unwrap().0;
let blocks1 = blocking_client.get_blocks(None).unwrap();
let blocks_async1 = async_client.get_blocks(None).await.unwrap();
assert_eq!(blocks1[0].time.height, start_height as u32);
let blocks1 = blocking_client.get_block_infos(None).unwrap();
let blocks_async1 = async_client.get_block_infos(None).await.unwrap();
assert_eq!(blocks1[0].height, start_height as u32);
assert_eq!(blocks1, blocks_async1);
env.mine_and_wait(1);

let blocks2 = blocking_client.get_blocks(None).unwrap();
let blocks_async2 = async_client.get_blocks(None).await.unwrap();
let blocks2 = blocking_client.get_block_infos(None).unwrap();
let blocks_async2 = async_client.get_block_infos(None).await.unwrap();
assert_eq!(blocks2, blocks_async2);
assert_ne!(blocks2, blocks1);

let blocks3 = blocking_client
.get_blocks(Some(start_height as u32))
.get_block_infos(Some(start_height as u32))
.unwrap();
let blocks_async3 = async_client
.get_blocks(Some(start_height as u32))
.get_block_infos(Some(start_height as u32))
.await
.unwrap();
assert_eq!(blocks3, blocks_async3);
assert_eq!(blocks3[0].time.height, start_height as u32);
assert_eq!(blocks3[0].height, start_height as u32);
assert_eq!(blocks3, blocks1);

let blocks_genesis = blocking_client.get_blocks(Some(0)).unwrap();
let blocks_genesis_async = async_client.get_blocks(Some(0)).await.unwrap();
let blocks_genesis = blocking_client.get_block_infos(Some(0)).unwrap();
let blocks_genesis_async = async_client.get_block_infos(Some(0)).await.unwrap();
assert_eq!(blocks_genesis, blocks_genesis_async);
}

Expand Down