diff --git a/src/api.rs b/src/api.rs index e7522de..971908c 100644 --- a/src/api.rs +++ b/src/api.rs @@ -200,6 +200,17 @@ pub trait ElectrumApi { /// Returns the merkle path for the transaction `txid` confirmed in the block at `height`. fn transaction_get_merkle(&self, txid: &Txid, height: usize) -> Result; + /// Returns a transaction hash, given a block `height` and a `tx_pos` in the block. + fn txid_from_pos(&self, height: usize, tx_pos: usize) -> Result; + + /// Returns a transaction hash and a merkle path, given a block `height` and a `tx_pos` in the + /// block. + fn txid_from_pos_with_merkle( + &self, + height: usize, + tx_pos: usize, + ) -> Result; + /// Returns the capabilities of the server. fn server_features(&self) -> Result; diff --git a/src/client.rs b/src/client.rs index 3511724..5cdf0d8 100644 --- a/src/client.rs +++ b/src/client.rs @@ -327,6 +327,20 @@ impl ElectrumApi for Client { impl_inner_call!(self, transaction_get_merkle, txid, height) } + #[inline] + fn txid_from_pos(&self, height: usize, tx_pos: usize) -> Result { + impl_inner_call!(self, txid_from_pos, height, tx_pos) + } + + #[inline] + fn txid_from_pos_with_merkle( + &self, + height: usize, + tx_pos: usize, + ) -> Result { + impl_inner_call!(self, txid_from_pos_with_merkle, height, tx_pos) + } + #[inline] fn server_features(&self) -> Result { impl_inner_call!(self, server_features) diff --git a/src/raw_client.rs b/src/raw_client.rs index 15a7234..eb78185 100644 --- a/src/raw_client.rs +++ b/src/raw_client.rs @@ -1102,6 +1102,38 @@ impl ElectrumApi for RawClient { Ok(serde_json::from_value(result)?) } + fn txid_from_pos(&self, height: usize, tx_pos: usize) -> Result { + let params = vec![Param::Usize(height), Param::Usize(tx_pos)]; + let req = Request::new_id( + self.last_id.fetch_add(1, Ordering::SeqCst), + "blockchain.transaction.id_from_pos", + params, + ); + let result = self.call(req)?; + + Ok(serde_json::from_value(result)?) + } + + fn txid_from_pos_with_merkle( + &self, + height: usize, + tx_pos: usize, + ) -> Result { + let params = vec![ + Param::Usize(height), + Param::Usize(tx_pos), + Param::Bool(true), + ]; + let req = Request::new_id( + self.last_id.fetch_add(1, Ordering::SeqCst), + "blockchain.transaction.id_from_pos", + params, + ); + let result = self.call(req)?; + + Ok(serde_json::from_value(result)?) + } + fn server_features(&self) -> Result { let req = Request::new_id( self.last_id.fetch_add(1, Ordering::SeqCst), @@ -1396,6 +1428,39 @@ mod test { )); } + #[test] + fn test_txid_from_pos() { + use bitcoin::Txid; + + let client = RawClient::new(get_test_server(), None).unwrap(); + + let txid = + Txid::from_str("1f7ff3c407f33eabc8bec7d2cc230948f2249ec8e591bcf6f971ca9366c8788d") + .unwrap(); + let resp = client.txid_from_pos(630000, 68).unwrap(); + assert_eq!(resp, txid); + } + + #[test] + fn test_txid_from_pos_with_merkle() { + use bitcoin::Txid; + + let client = RawClient::new(get_test_server(), None).unwrap(); + + let txid = + Txid::from_str("1f7ff3c407f33eabc8bec7d2cc230948f2249ec8e591bcf6f971ca9366c8788d") + .unwrap(); + let resp = client.txid_from_pos_with_merkle(630000, 68).unwrap(); + assert_eq!(resp.tx_hash, txid); + assert_eq!( + resp.merkle[0], + [ + 34, 65, 51, 64, 49, 139, 115, 189, 185, 246, 70, 225, 168, 193, 217, 195, 47, 66, + 179, 240, 153, 24, 114, 215, 144, 196, 212, 41, 39, 155, 246, 25 + ] + ); + } + #[test] fn test_ping() { let client = RawClient::new(get_test_server(), None).unwrap(); diff --git a/src/types.rs b/src/types.rs index 84f9c94..ce3ef9f 100644 --- a/src/types.rs +++ b/src/types.rs @@ -240,6 +240,17 @@ pub struct GetMerkleRes { pub merkle: Vec<[u8; 32]>, } +/// Response to a [`txid_from_pos_with_merkle`](../client/struct.Client.html#method.txid_from_pos_with_merkle) +/// request. +#[derive(Clone, Debug, Deserialize)] +pub struct TxidFromPosRes { + /// Txid of the transaction. + pub tx_hash: Txid, + /// The merkle path of the transaction. + #[serde(deserialize_with = "from_hex_array")] + pub merkle: Vec<[u8; 32]>, +} + /// Notification of a new block header #[derive(Clone, Debug, Deserialize)] pub struct HeaderNotification {