bitcoin_blk_reader/
bitcoin_rest.rs1
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 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}