Skip to content

Commit 1a73574

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 1a73574

File tree

3 files changed

+78
-2
lines changed

3 files changed

+78
-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: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,46 @@ mod test {
568568
assert!(merkle_proof.pos > 0);
569569
}
570570

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

0 commit comments

Comments
 (0)