use errors::TxLogErrors;
use hex;
use serde::{Deserialize, Serialize};
mod errors;
#[derive(Clone)]
pub struct TxlogClient {
pub url: String,
secret: String,
}
#[derive(Serialize, Deserialize)]
pub struct SubmitResponse {
pub success: bool,
}
#[derive(Serialize, Deserialize)]
pub struct TxResponse {
pub txid: String,
pub status: u32,
pub created: String,
pub updated: String,
}
pub fn new(url: String, secret: String) -> TxlogClient {
return TxlogClient { url, secret };
}
pub struct SubmitMetadata {
pub metadata: Option<String>,
pub merchant: Option<String>,
pub network: Option<String>,
}
impl TxlogClient {
pub async fn rawtx(&self, txid: &String) -> Result<Vec<u8>, TxLogErrors> {
let client = reqwest::Client::new();
let url = format!("{}/tx/{}/raw", &self.url, txid);
let response = client
.get(&url)
.header("Authorization", format!("Bearer {}", self.secret))
.send()
.await?;
let body = match response.text().await {
Err(e) => return Err(TxLogErrors::BodyDecodeError(e)),
Ok(v) => v,
};
let decoded = match hex::decode(&body) {
Ok(v) => v,
Err(e) => return Err(TxLogErrors::HexDecode(e)),
};
return Ok(decoded);
}
pub async fn tx(&self, txid: &String) -> Result<TxResponse, TxLogErrors> {
let client = reqwest::Client::new();
let url = format!("{}/tx/{}", &self.url, txid);
let response = client
.get(&url)
.header("Authorization", format!("Bearer {}", self.secret))
.send()
.await?;
let res = match response.json::<TxResponse>().await {
Ok(v) => v,
Err(e) => return Err(TxLogErrors::BodyDecodeError(e)),
};
Ok(res)
}
pub async fn submit(
&self,
rawtx: Vec<u8>,
params: Option<SubmitMetadata>,
) -> Result<SubmitResponse, TxLogErrors> {
let client = reqwest::Client::new();
let url = format!("{}/tx", &self.url);
let mut query_params: Vec<(String, String)> = vec![];
match params {
Some(p) => {
match p.metadata {
Some(v) => query_params.push(("metadata".to_string(), v)),
None => (),
};
match p.merchant {
Some(v) => query_params.push(("merchant".to_string(), v)),
None => (),
};
match p.network {
Some(v) => query_params.push(("network".to_string(), v)),
None => (),
};
}
None => (),
}
let response = client
.post(&url)
.query(&query_params)
.header("Authorization", format!("Bearer {}", self.secret))
.header("Content-Type", "application/octet-stream")
.body(rawtx)
.send()
.await?;
let res = match response.json::<SubmitResponse>().await {
Ok(v) => v,
Err(e) => return Err(TxLogErrors::BodyDecodeError(e)),
};
if !res.success {
return Err(TxLogErrors::SubmitResponseFailed);
}
Ok(res)
}
}
#[cfg(test)]
mod tests {
macro_rules! aw {
($e:expr) => {
tokio_test::block_on($e)
};
}
#[test]
fn test_rawtx() {
let secret = dotenv::var("TXLOG_SECRET").unwrap();
let url = dotenv::var("TXLOG_URL").unwrap();
let rawtx = dotenv::var("RAWTX").unwrap();
let txid = dotenv::var("TXID").unwrap();
let client = crate::new(url, secret);
let response = aw!(client.rawtx(&txid)).unwrap();
assert_eq!(response, hex::decode(&rawtx).unwrap());
}
#[test]
fn test_submit() {
let secret = dotenv::var("TXLOG_SECRET").unwrap();
let url = dotenv::var("TXLOG_URL").unwrap();
let rawtx = dotenv::var("RAWTX").unwrap();
let client = crate::new(url, secret);
let response = aw!(client.submit(hex::decode(&rawtx).unwrap(), None)).unwrap();
assert_eq!(response.success, true);
}
#[test]
fn test_tx() {
let secret = dotenv::var("TXLOG_SECRET").unwrap();
let url = dotenv::var("TXLOG_URL").unwrap();
let txid = dotenv::var("TXID").unwrap();
let client = crate::new(url, secret);
let response = aw!(client.tx(&txid)).unwrap();
assert_eq!(response.txid, txid);
}
}