bitcoin_blk_reader/
bitcoin_rest.rs

1
2use std::time::{
3    Duration,
4};
5use bytes::Bytes;
6use reqwest::{
7    Response,
8    StatusCode,
9};
10use bitcoin_hashes::Sha256d;
11
12#[derive(Debug)]
13pub enum BitcoinRestError {
14	Reqwest(reqwest::Error),
15	Response(Response),
16}
17
18impl std::fmt::Display for BitcoinRestError {
19    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
20        match self {
21            Self::Reqwest(e) => write!(f, "Reqwest error: {}", e),
22            Self::Response(e) => write!(f, "Response error: {}", e.status()),
23        }
24    }
25}
26
27impl std::error::Error for BitcoinRestError {}
28
29impl From<reqwest::Error> for BitcoinRestError {
30	fn from(e: reqwest::Error) -> Self {
31		Self::Reqwest(e)
32	}
33}
34
35impl From<Response> for BitcoinRestError {
36	fn from(e: Response) -> Self {
37		Self::Response(e)
38	}
39}
40
41#[derive(Debug, Clone)]
42pub struct BitcoinRest {
43    client: reqwest::Client,
44    rest_endpoint: String,
45}
46
47impl BitcoinRest {
48    pub fn new(rest_endpoint: String) -> Self {
49        let client = reqwest::Client::builder()
50            .timeout(Duration::from_secs(10))
51            .build()
52            .unwrap()
53            ;
54        Self {
55            client,
56            rest_endpoint,
57        }
58    }
59    pub async fn fetch(&self, path: &[&str], ext: &str, query: Option<&str>) -> Result<Response, reqwest::Error> {
60        let mut url = format!("{}/{}.{}", self.rest_endpoint, path.join("/"), ext);
61        if let Some(query) = query {
62            url.push_str(&format!("?{}", query));
63        }
64				return Ok(self.client.get(&url).send().await?);
65    }
66    pub async fn fetch_hex(&self, path: &[&str], query: Option<&str>) -> Result<String, BitcoinRestError> {
67        let response = self.fetch(path, "hex", query).await?;
68        if response.status() != StatusCode::OK {
69            return Err(response.into());
70        }
71        let hex = response.text().await.unwrap().trim().to_string();
72        Ok(hex)
73    }
74    pub async fn fetch_bin(&self, path: &[&str], query: Option<&str>) -> Result<Bytes, BitcoinRestError> {
75        let response = self.fetch(path, "bin", query).await?;
76        if response.status() != StatusCode::OK {
77            return Err(response.into());
78        }
79        let bytes = response.bytes().await.unwrap();
80        Ok(bytes)
81    }
82    pub async fn get_block(&self, mut hash: [u8; 32]) -> Result<Bytes, BitcoinRestError> {
83        hash.reverse();
84        let block = self.fetch_bin(&["block", &hex::encode(hash)], None).await?;
85        Ok(block)
86    }
87    pub async fn get_blockhashbyheight(&self, height: u32) -> Result<[u8; 32], BitcoinRestError> {
88        let block_hash = self.fetch_bin(&["blockhashbyheight", &height.to_string()], None).await?;
89        if block_hash.len() != 32 {
90            panic!("Invalid block hash length");
91        }
92        let mut hash = [0u8; 32];
93        hash.copy_from_slice(&block_hash);
94        Ok(hash)
95    }
96    pub async fn get_headers(&self, mut hash: [u8; 32], count: u32) -> Result<Vec<[u8; 80]>, BitcoinRestError> {
97        hash.reverse();
98        let mut headers = Vec::new();
99        let headers_bytes = self.fetch_bin(&["headers", &hex::encode(hash)], Some(format!("count={}", count).as_str())).await?;
100        if headers_bytes.len() % 80 != 0 {
101            panic!("Invalid headers length");
102        }
103        for i in 0..(headers_bytes.len() / 80) {
104            let mut header = [0u8; 80];
105            header.copy_from_slice(&headers_bytes[(i * 80)..((i + 1) * 80)]);
106            headers.push(header);
107        }
108        Ok(headers)
109    }
110    pub async fn get_all_headers(&self, mut hash: [u8; 32], count: Option<u32>) -> Result<Vec<[u8; 80]>, BitcoinRestError> {
111        let mut result = Vec::new();
112        let count = count.unwrap_or(2000);
113        let mut is_first = true;
114        loop {
115            let mut headers = self.get_headers(hash, count).await?;
116            let headers_len = headers.len();
117            if headers_len == 0 {
118                break;
119            }
120            // Drop first header on non-first iteration.
121            if !is_first {
122                headers = headers[1..].to_vec();
123            }
124            is_first = false;
125            hash = Sha256d::hash(headers.last().unwrap()).to_byte_array();
126            result.push(headers);
127            if headers_len < count as usize {
128                break;
129            }
130        }
131        let headers = result.concat();
132        Ok(headers)
133    }
134}