Skip to content

Commit

Permalink
feat: add id_from_pos support
Browse files Browse the repository at this point in the history
  • Loading branch information
LagginTimes committed Nov 1, 2024
1 parent 0b97659 commit 87e1cca
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 18 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 transaction_id_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 transaction_id_from_pos_with_merkle(
&self,
height: usize,
tx_pos: usize,
) -> Result<GetIdFromPosRes, 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 transaction_id_from_pos(&self, height: usize, tx_pos: usize) -> Result<Txid, Error> {
impl_inner_call!(self, transaction_id_from_pos, height, tx_pos)
}

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

#[inline]
fn server_features(&self) -> Result<ServerFeaturesRes, Error> {
impl_inner_call!(self, server_features)
Expand Down
112 changes: 94 additions & 18 deletions src/raw_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -299,46 +299,52 @@ impl RawClient<ElectrumSslStream> {
))]
mod danger {
use crate::raw_client::ServerName;
use rustls::client::danger::ServerCertVerified;
use rustls::pki_types::CertificateDer;
use rustls::pki_types::UnixTime;
use rustls::Error;
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified};
use rustls::crypto::CryptoProvider;
use rustls::pki_types::{CertificateDer, UnixTime};
use rustls::DigitallySignedStruct;

#[derive(Debug)]
pub struct NoCertificateVerification {}
pub struct NoCertificateVerification(CryptoProvider);

impl NoCertificateVerification {
pub fn new(provider: CryptoProvider) -> Self {
Self(provider)
}
}

impl rustls::client::danger::ServerCertVerifier for NoCertificateVerification {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer,
_intermediates: &[CertificateDer],
_server_name: &ServerName,
_ocsp_response: &[u8],
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
_server_name: &ServerName<'_>,
_ocsp: &[u8],
_now: UnixTime,
) -> Result<ServerCertVerified, Error> {
) -> Result<ServerCertVerified, rustls::Error> {
Ok(ServerCertVerified::assertion())
}

fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
}

fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &rustls::DigitallySignedStruct,
) -> Result<rustls::client::danger::HandshakeSignatureValid, Error> {
Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
_dss: &DigitallySignedStruct,
) -> Result<HandshakeSignatureValid, rustls::Error> {
Ok(HandshakeSignatureValid::assertion())
}

fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
vec![]
self.0.signature_verification_algorithms.supported_schemes()
}
}
}
Expand Down Expand Up @@ -420,7 +426,10 @@ impl RawClient<ElectrumSslStream> {
builder
.dangerous()
.with_custom_certificate_verifier(std::sync::Arc::new(
danger::NoCertificateVerification {},
#[cfg(feature = "use-rustls")]
danger::NoCertificateVerification::new(rustls::crypto::aws_lc_rs::default_provider()),
#[cfg(feature = "use-rustls-ring")]
danger::NoCertificateVerification::new(rustls::crypto::ring::default_provider()),
))
.with_no_client_auth()
};
Expand Down Expand Up @@ -1093,6 +1102,38 @@ impl<T: Read + Write> ElectrumApi for RawClient<T> {
Ok(serde_json::from_value(result)?)
}

fn transaction_id_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 transaction_id_from_pos_with_merkle(
&self,
height: usize,
tx_pos: usize,
) -> Result<GetIdFromPosRes, 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 @@ -1387,6 +1428,41 @@ mod test {
));
}

#[test]
fn test_transaction_id_from_pos() {
use bitcoin::Txid;

let client = RawClient::new(get_test_server(), None).unwrap();

let txid =
Txid::from_str("1f7ff3c407f33eabc8bec7d2cc230948f2249ec8e591bcf6f971ca9366c8788d")
.unwrap();
let resp = client.transaction_id_from_pos(630000, 68).unwrap();
assert_eq!(resp, txid);
}

#[test]
fn test_transaction_id_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
.transaction_id_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 [`transaction_id_from_pos_with_merkle`](../client/struct.Client.html#method.transaction_id_from_pos_with_merkle)
/// request.
#[derive(Clone, Debug, Deserialize)]
pub struct GetIdFromPosRes {
/// 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 87e1cca

Please sign in to comment.