Skip to content

Commit 9942f16

Browse files
committed
Add get_address_stats and get_address_txns endpoints
1 parent 1f4acad commit 9942f16

File tree

4 files changed

+132
-0
lines changed

4 files changed

+132
-0
lines changed

src/api.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,31 @@ pub struct BlockSummary {
9797
pub merkle_root: bitcoin::hash_types::TxMerkleNode,
9898
}
9999

100+
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
101+
pub struct AddressStats {
102+
pub address: String,
103+
pub chain_stats: ChainStats,
104+
pub mempool_stats: MempoolStats,
105+
}
106+
107+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
108+
pub struct ChainStats {
109+
pub funded_txo_count: u32,
110+
pub funded_txo_sum: u64,
111+
pub spent_txo_count: u32,
112+
pub spent_txo_sum: u64,
113+
pub tx_count: u32,
114+
}
115+
116+
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
117+
pub struct MempoolStats {
118+
pub funded_txo_count: u32,
119+
pub funded_txo_sum: u64,
120+
pub spent_txo_count: u32,
121+
pub spent_txo_sum: u64,
122+
pub tx_count: u32,
123+
}
124+
100125
impl Tx {
101126
pub fn to_tx(&self) -> Transaction {
102127
Transaction {

src/async.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
//! Esplora by way of `reqwest` HTTP client.
1313
1414
use async_std::task;
15+
use bitcoin::Address;
1516
use std::collections::HashMap;
1617
use std::str::FromStr;
1718

@@ -27,6 +28,7 @@ use log::{debug, error, info, trace};
2728

2829
use reqwest::{header, Client, Response};
2930

31+
use crate::api::AddressStats;
3032
use crate::{
3133
BlockStatus, BlockSummary, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus,
3234
BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
@@ -378,6 +380,20 @@ impl AsyncClient {
378380
.map(|block_hash| BlockHash::from_str(&block_hash).map_err(Error::HexToArray))?
379381
}
380382

383+
/// Get information about a specific address, includes confirmed balance and transactions in
384+
/// the mempool.
385+
pub async fn get_address_stats(&self, address: &Address) -> Result<AddressStats, Error> {
386+
let path = format!("/address/{address}");
387+
self.get_response_json(&path).await
388+
}
389+
390+
/// Get transaction history for the specified address/scripthash, sorted with newest first.
391+
/// Returns up to 50 mempool transactions plus the first 25 confirmed transactions.
392+
pub async fn get_address_txns(&self, address: &Address) -> Result<Vec<Tx>, Error> {
393+
let path = format!("/address/{address}/txs");
394+
self.get_response_json(&path).await
395+
}
396+
381397
/// Get confirmed transaction history for the specified address/scripthash,
382398
/// sorted with newest first. Returns 25 transactions per page.
383399
/// More can be requested by specifying the last txid seen by the previous

src/blocking.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use std::convert::TryFrom;
1616
use std::str::FromStr;
1717
use std::thread;
1818

19+
use bitcoin::Address;
1920
#[allow(unused_imports)]
2021
use log::{debug, error, info, trace};
2122

@@ -28,6 +29,7 @@ use bitcoin::{
2829
block::Header as BlockHeader, Block, BlockHash, MerkleBlock, Script, Transaction, Txid,
2930
};
3031

32+
use crate::api::AddressStats;
3133
use crate::{
3234
BlockStatus, BlockSummary, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus,
3335
BASE_BACKOFF_MILLIS, RETRYABLE_ERROR_CODES,
@@ -317,6 +319,20 @@ impl BlockingClient {
317319
self.get_response_json("/fee-estimates")
318320
}
319321

322+
/// Get information about a specific address, includes confirmed balance and transactions in
323+
/// the mempool.
324+
pub fn get_address_stats(&self, address: &Address) -> Result<AddressStats, Error> {
325+
let path = format!("/address/{address}");
326+
self.get_response_json(&path)
327+
}
328+
329+
/// Get transaction history for the specified address/scripthash, sorted with newest first.
330+
/// Returns up to 50 mempool transactions plus the first 25 confirmed transactions.
331+
pub fn get_address_txns(&self, address: &Address) -> Result<Vec<Tx>, Error> {
332+
let path = format!("/address/{address}/txs");
333+
self.get_response_json(&path)
334+
}
335+
320336
/// Get confirmed transaction history for the specified address/scripthash,
321337
/// sorted with newest first. Returns 25 transactions per page.
322338
/// More can be requested by specifying the last txid seen by the previous

src/lib.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,4 +992,79 @@ mod test {
992992
let tx_async = async_client.get_tx(&txid).await.unwrap();
993993
assert_eq!(tx, tx_async);
994994
}
995+
996+
#[cfg(all(feature = "blocking", feature = "async"))]
997+
#[tokio::test]
998+
async fn test_get_address_stats() {
999+
let (blocking_client, async_client) = setup_clients().await;
1000+
1001+
let address = BITCOIND
1002+
.client
1003+
.get_new_address(Some("test"), Some(AddressType::Legacy))
1004+
.unwrap()
1005+
.assume_checked();
1006+
1007+
let _txid = BITCOIND
1008+
.client
1009+
.send_to_address(
1010+
&address,
1011+
Amount::from_sat(1000),
1012+
None,
1013+
None,
1014+
None,
1015+
None,
1016+
None,
1017+
None,
1018+
)
1019+
.unwrap();
1020+
1021+
let address_blocking = blocking_client.get_address_stats(&address).unwrap();
1022+
let address_async = async_client.get_address_stats(&address).await.unwrap();
1023+
assert_eq!(address_blocking, address_async);
1024+
assert_eq!(address_async.chain_stats.funded_txo_count, 0);
1025+
1026+
let _miner = MINER.lock().await;
1027+
generate_blocks_and_wait(1);
1028+
1029+
let address_blocking = blocking_client.get_address_stats(&address).unwrap();
1030+
let address_async = async_client.get_address_stats(&address).await.unwrap();
1031+
assert_eq!(address_blocking, address_async);
1032+
assert_eq!(address_async.chain_stats.funded_txo_count, 1);
1033+
assert_eq!(address_async.chain_stats.funded_txo_sum, 1000);
1034+
}
1035+
1036+
#[cfg(all(feature = "blocking", feature = "async"))]
1037+
#[tokio::test]
1038+
async fn test_get_address_txns() {
1039+
let (blocking_client, async_client) = setup_clients().await;
1040+
1041+
let address = BITCOIND
1042+
.client
1043+
.get_new_address(Some("test"), Some(AddressType::Legacy))
1044+
.unwrap()
1045+
.assume_checked();
1046+
1047+
let txid = BITCOIND
1048+
.client
1049+
.send_to_address(
1050+
&address,
1051+
Amount::from_sat(1000),
1052+
None,
1053+
None,
1054+
None,
1055+
None,
1056+
None,
1057+
None,
1058+
)
1059+
.unwrap();
1060+
1061+
let _miner = MINER.lock().await;
1062+
generate_blocks_and_wait(1);
1063+
1064+
let address_txns_blocking = blocking_client.get_address_txns(&address).unwrap();
1065+
let address_txns_async = async_client.get_address_txns(&address).await.unwrap();
1066+
1067+
assert_eq!(address_txns_blocking, address_txns_async);
1068+
assert_eq!(address_txns_async[0].txid, txid);
1069+
}
9951070
}

0 commit comments

Comments
 (0)