Skip to content

Commit bce24eb

Browse files
committed
Merge #87: feat: add way to make raw req and get url for BlockingClient
3b778c5 feat: add method for getting tx info via `GET /tx/:txid` (志宇) bc3d431 feat: add way to make raw req and get url for `BlockingClient` (志宇) Pull request description: Also add method for getting from `/tx/:txid` endpoint. ACKs for top commit: evanlinjin: self-ACK 3b778c5 Tree-SHA512: 81ab7af85b40fba0ec20194649d2f95b8e5fcd66c856ffb1d10ab9f81cddbfd11ef6ff399bd542e7b920a426ccbd9c114152ebd170bc8e6b3562b44e16ce612d
2 parents 6440e1d + 3b778c5 commit bce24eb

File tree

4 files changed

+100
-2
lines changed

4 files changed

+100
-2
lines changed

src/api.rs

+14-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
pub use bitcoin::consensus::{deserialize, serialize};
66
pub use bitcoin::hex::FromHex;
7+
use bitcoin::Weight;
78
pub use bitcoin::{
89
transaction, Amount, BlockHash, OutPoint, ScriptBuf, Transaction, TxIn, TxOut, Txid, Witness,
910
};
@@ -65,13 +66,17 @@ pub struct BlockStatus {
6566
pub next_best: Option<BlockHash>,
6667
}
6768

68-
#[derive(Deserialize, Clone, Debug)]
69+
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
6970
pub struct Tx {
7071
pub txid: Txid,
7172
pub version: i32,
7273
pub locktime: u32,
7374
pub vin: Vec<Vin>,
7475
pub vout: Vec<Vout>,
76+
/// Transaction size in raw bytes (NOT virtual bytes).
77+
pub size: usize,
78+
/// Transaction weight units.
79+
pub weight: u64,
7580
pub status: TxStatus,
7681
pub fee: u64,
7782
}
@@ -147,6 +152,14 @@ impl Tx {
147152
})
148153
.collect()
149154
}
155+
156+
pub fn weight(&self) -> Weight {
157+
Weight::from_wu(self.weight)
158+
}
159+
160+
pub fn fee(&self) -> Amount {
161+
Amount::from_sat(self.fee)
162+
}
150163
}
151164

152165
fn deserialize_witness<'de, D>(d: D) -> Result<Vec<Vec<u8>>, D::Error>

src/async.rs

+20
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,26 @@ impl AsyncClient {
143143
}
144144
}
145145

146+
/// Get transaction info given it's [`Txid`].
147+
pub async fn get_tx_info(&self, txid: &Txid) -> Result<Option<Tx>, Error> {
148+
let resp = self
149+
.client
150+
.get(&format!("{}/tx/{}", self.url, txid))
151+
.send()
152+
.await?;
153+
if resp.status() == StatusCode::NOT_FOUND {
154+
return Ok(None);
155+
}
156+
if resp.status().is_server_error() || resp.status().is_client_error() {
157+
Err(Error::HttpResponse {
158+
status: resp.status().as_u16(),
159+
message: resp.text().await?,
160+
})
161+
} else {
162+
Ok(Some(resp.json().await?))
163+
}
164+
}
165+
146166
/// Get a [`BlockHeader`] given a particular block hash.
147167
pub async fn get_header_by_hash(&self, block_hash: &BlockHash) -> Result<BlockHeader, Error> {
148168
let resp = self

src/blocking.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,13 @@ impl BlockingClient {
5151
}
5252
}
5353

54-
fn get_request(&self, path: &str) -> Result<Request, Error> {
54+
/// Get the underlying base URL.
55+
pub fn url(&self) -> &str {
56+
&self.url
57+
}
58+
59+
/// Perform a raw HTTP GET request with the given URI `path`.
60+
pub fn get_request(&self, path: &str) -> Result<Request, Error> {
5561
let mut request = minreq::get(format!("{}{}", self.url, path));
5662

5763
if let Some(proxy) = &self.proxy {
@@ -207,6 +213,11 @@ impl BlockingClient {
207213
self.get_response_json(&format!("/tx/{}/status", txid))
208214
}
209215

216+
/// Get transaction info given it's [`Txid`].
217+
pub fn get_tx_info(&self, txid: &Txid) -> Result<Option<Tx>, Error> {
218+
self.get_opt_response_json(&format!("/tx/{}", txid))
219+
}
220+
210221
/// Get a [`BlockHeader`] given a particular block hash.
211222
pub fn get_header_by_hash(&self, block_hash: &BlockHash) -> Result<BlockHeader, Error> {
212223
self.get_response_hex(&format!("/block/{}/header", block_hash))

src/lib.rs

+54
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,60 @@ mod test {
503503
assert!(tx_status.block_time.is_none());
504504
}
505505

506+
#[cfg(all(feature = "blocking", feature = "async"))]
507+
#[tokio::test]
508+
async fn test_get_tx_info() {
509+
let (blocking_client, async_client) = setup_clients().await;
510+
511+
let address = BITCOIND
512+
.client
513+
.get_new_address(Some("test"), Some(AddressType::Legacy))
514+
.unwrap()
515+
.assume_checked();
516+
let txid = BITCOIND
517+
.client
518+
.send_to_address(
519+
&address,
520+
Amount::from_sat(1000),
521+
None,
522+
None,
523+
None,
524+
None,
525+
None,
526+
None,
527+
)
528+
.unwrap();
529+
let _miner = MINER.lock().await;
530+
generate_blocks_and_wait(1);
531+
532+
let tx_res = BITCOIND.client.get_transaction(&txid, None).unwrap();
533+
let tx_exp = tx_res.transaction().expect("must decode");
534+
535+
let tx_info = blocking_client
536+
.get_tx_info(&txid)
537+
.unwrap()
538+
.expect("must get tx");
539+
let tx_info_async = async_client
540+
.get_tx_info(&txid)
541+
.await
542+
.unwrap()
543+
.expect("must get tx");
544+
assert_eq!(tx_info, tx_info_async);
545+
assert_eq!(tx_info.txid, txid);
546+
assert_eq!(tx_info.to_tx(), tx_exp);
547+
assert_eq!(tx_info.size, tx_exp.total_size());
548+
assert_eq!(tx_info.weight(), tx_exp.weight());
549+
assert_eq!(tx_info.fee(), tx_res.fee.unwrap().unsigned_abs());
550+
assert!(tx_info.status.confirmed);
551+
assert_eq!(tx_info.status.block_height, tx_res.info.blockheight);
552+
assert_eq!(tx_info.status.block_hash, tx_res.info.blockhash);
553+
assert_eq!(tx_info.status.block_time, tx_res.info.blocktime);
554+
555+
let txid = Txid::hash(b"not exist");
556+
assert_eq!(blocking_client.get_tx_info(&txid).unwrap(), None);
557+
assert_eq!(async_client.get_tx_info(&txid).await.unwrap(), None);
558+
}
559+
506560
#[cfg(all(feature = "blocking", feature = "async"))]
507561
#[tokio::test]
508562
async fn test_get_header_by_hash() {

0 commit comments

Comments
 (0)