Skip to content

Commit e40583a

Browse files
committed
Implement get_merkle_block API call
We already have the `get_merkle_proof` call, but this one returns a `bitcoin::MerkleBlock`, which has additional data and is more often than not more useful than Electrum's counterpart.
1 parent e24663b commit e40583a

File tree

3 files changed

+81
-2
lines changed

3 files changed

+81
-2
lines changed

src/async.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use std::str::FromStr;
1717
use bitcoin::consensus::{deserialize, serialize};
1818
use bitcoin::hashes::hex::{FromHex, ToHex};
1919
use bitcoin::hashes::{sha256, Hash};
20-
use bitcoin::{Block, BlockHash, BlockHeader, Script, Transaction, Txid};
20+
use bitcoin::{Block, BlockHash, BlockHeader, MerkleBlock, Script, Transaction, Txid};
2121

2222
#[allow(unused_imports)]
2323
use log::{debug, error, info, trace};
@@ -176,6 +176,23 @@ impl AsyncClient {
176176
Ok(Some(resp.error_for_status()?.json().await?))
177177
}
178178

179+
/// Get a [`MerkleBlock`] inclusion proof for a [`Transaction`] with the given [`Txid`].
180+
pub async fn get_merkle_block(&self, tx_hash: &Txid) -> Result<Option<MerkleBlock>, Error> {
181+
let resp = self
182+
.client
183+
.get(&format!("{}/tx/{}/merkleblock-proof", self.url, tx_hash))
184+
.send()
185+
.await?;
186+
187+
if let StatusCode::NOT_FOUND = resp.status() {
188+
return Ok(None);
189+
}
190+
191+
let merkle_block = deserialize(&Vec::from_hex(&resp.text().await?)?)?;
192+
193+
Ok(Some(merkle_block))
194+
}
195+
179196
/// Get the spending status of an output given a [`Txid`] and the output index.
180197
pub async fn get_output_status(
181198
&self,

src/blocking.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use ureq::{Agent, Proxy, Response};
2525
use bitcoin::consensus::{deserialize, serialize};
2626
use bitcoin::hashes::hex::{FromHex, ToHex};
2727
use bitcoin::hashes::{sha256, Hash};
28-
use bitcoin::{Block, BlockHash, BlockHeader, Script, Transaction, Txid};
28+
use bitcoin::{Block, BlockHash, BlockHeader, MerkleBlock, Script, Transaction, Txid};
2929

3030
use crate::{BlockStatus, Builder, Error, MerkleProof, OutputStatus, Tx, TxStatus};
3131

@@ -202,6 +202,25 @@ impl BlockingClient {
202202
}
203203
}
204204

205+
/// Get a [`MerkleBlock`] inclusion proof for a [`Transaction`] with the given [`Txid`].
206+
pub fn get_merkle_block(&self, txid: &Txid) -> Result<Option<MerkleBlock>, Error> {
207+
let resp = self
208+
.agent
209+
.get(&format!("{}/tx/{}/merkleblock-proof", self.url, txid))
210+
.call();
211+
212+
match resp {
213+
Ok(resp) => Ok(Some(deserialize(&Vec::from_hex(&resp.into_string()?)?)?)),
214+
Err(ureq::Error::Status(code, _)) => {
215+
if is_status_not_found(code) {
216+
return Ok(None);
217+
}
218+
Err(Error::HttpResponse(code))
219+
}
220+
Err(e) => Err(Error::Ureq(e)),
221+
}
222+
}
223+
205224
/// Get the spending status of an output given a [`Txid`] and the output index.
206225
pub fn get_output_status(
207226
&self,

src/lib.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
//! * `async-https` enables [`reqwest`], the async client with support for proxying and TLS (SSL).
4343
//!
4444
//!
45+
46+
#![allow(clippy::result_large_err)]
47+
4548
use std::collections::HashMap;
4649
use std::fmt;
4750
use std::io;
@@ -568,6 +571,46 @@ mod test {
568571
assert!(merkle_proof.pos > 0);
569572
}
570573

574+
#[cfg(all(feature = "blocking", any(feature = "async", feature = "async-https")))]
575+
#[tokio::test]
576+
async fn test_get_merkle_block() {
577+
let (blocking_client, async_client) = setup_clients().await;
578+
579+
let address = BITCOIND
580+
.client
581+
.get_new_address(Some("test"), Some(AddressType::Legacy))
582+
.unwrap();
583+
let txid = BITCOIND
584+
.client
585+
.send_to_address(
586+
&address,
587+
Amount::from_sat(1000),
588+
None,
589+
None,
590+
None,
591+
None,
592+
None,
593+
None,
594+
)
595+
.unwrap();
596+
let _miner = MINER.lock().await;
597+
generate_blocks_and_wait(1);
598+
599+
let merkle_block = blocking_client.get_merkle_block(&txid).unwrap().unwrap();
600+
let merkle_block_async = async_client.get_merkle_block(&txid).await.unwrap().unwrap();
601+
assert_eq!(merkle_block, merkle_block_async);
602+
603+
let mut matches = vec![txid];
604+
let mut indexes = vec![];
605+
let root = merkle_block
606+
.txn
607+
.extract_matches(&mut matches, &mut indexes)
608+
.unwrap();
609+
assert_eq!(root, merkle_block.header.merkle_root);
610+
assert_eq!(indexes.len(), 1);
611+
assert!(indexes[0] > 0);
612+
}
613+
571614
#[cfg(all(feature = "blocking", any(feature = "async", feature = "async-https")))]
572615
#[tokio::test]
573616
async fn test_get_output_status() {

0 commit comments

Comments
 (0)