Skip to content

Commit

Permalink
Merge #155: feat: add id_from_pos support
Browse files Browse the repository at this point in the history
a871b08 feat: add `id_from_pos` support (Wei Chen)

Pull request description:

  This PR introduces the `blockchain.transaction.id_from_pos` feature from Electrum. This functionality is essential for an ongoing implementation in `bdk_electrum`, which requires retrieving the coinbase transaction from a block at a specified `height`. Currently, there is no straightforward method to achieve this. By implementing the `id_from_pos` feature, we will effectively address this gap.

ACKs for top commit:
  oleonardolima:
    utACK a871b08
  ValuedMammal:
    ACK a871b08
  evanlinjin:
    ACK a871b08

Tree-SHA512: e8f1ab44473f67030b4f19e766fcee472b7cfc1dac6df8c9f0e08d966e0ed48289de03768771623b9fc7e7ef15dacdb7e3b26c04f9b0de7566d67cc7585a7fa6
  • Loading branch information
ValuedMammal committed Nov 19, 2024
2 parents 6fe96fd + a871b08 commit 1fcddcb
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<GetMerkleRes, Error>;

/// 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<Txid, Error>;

/// 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<TxidFromPosRes, Error>;

/// Returns the capabilities of the server.
fn server_features(&self) -> Result<ServerFeaturesRes, Error>;

Expand Down
14 changes: 14 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Txid, Error> {
impl_inner_call!(self, txid_from_pos, height, tx_pos)
}

#[inline]
fn txid_from_pos_with_merkle(
&self,
height: usize,
tx_pos: usize,
) -> Result<TxidFromPosRes, Error> {
impl_inner_call!(self, txid_from_pos_with_merkle, height, tx_pos)
}

#[inline]
fn server_features(&self) -> Result<ServerFeaturesRes, Error> {
impl_inner_call!(self, server_features)
Expand Down
65 changes: 65 additions & 0 deletions src/raw_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,38 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
Ok(serde_json::from_value(result)?)
}

fn txid_from_pos(&self, height: usize, tx_pos: usize) -> Result<Txid, Error> {
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<TxidFromPosRes, Error> {
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<ServerFeaturesRes, Error> {
let req = Request::new_id(
self.last_id.fetch_add(1, Ordering::SeqCst),
Expand Down Expand Up @@ -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();
Expand Down
11 changes: 11 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 1fcddcb

Please sign in to comment.