use itertools::Itertools;
use nintypes::common::hash::{Hash256, HashError};
use crate::{errors::ClientResult, BlockHeight, Client, ClientError, HasBlockInfo};
use super::{BlockMeta, BlockMetaWithoutPrevHash};
impl<T: HasBlockInfo> Client<T> {
async fn get_block_hash_by_height(&self, height: BlockHeight) -> ClientResult<Hash256> {
let url = &self.config.url;
let response = self
.client
.execute(
self.client
.get(format!("{url}/block-height/{height}"))
.build()?,
)
.await?;
let status = response.status();
if !status.is_success() {
if status.as_u16() == 404 {
return Err(ClientError::BlockNotFound(height));
} else {
let r = response.text().await.unwrap_or_default();
return Err(ClientError::RequestUnsucces(status, r));
}
}
let bytes = response.bytes().await?;
let block_hash = hex_from_electrs_to_hash(&bytes)?;
Ok(block_hash)
}
pub async fn get_electrs_block_meta(&self, height: BlockHeight) -> ClientResult<BlockMeta> {
let hash = self.get_block_hash_by_height(height).await?;
let prev_block_hash = self
.get_block_hash_by_height(height.saturating_sub(1))
.await?;
Ok(BlockMeta {
height,
hash,
prev_block_hash,
})
}
pub async fn get_last_electrs_block_meta(&self) -> ClientResult<BlockMeta> {
let url = &self.config.url;
let response = self
.client
.execute(self.client.get(format!("{url}/status")).build()?)
.await?
.text()
.await?;
let BlockMetaWithoutPrevHash {
ord_height: height,
ord_tip: tip,
} = serde_json::from_str(&response)?;
let hash = hex_from_electrs_to_hash(tip.as_bytes())?;
let prev_block_hash = self
.get_block_hash_by_height(height.saturating_sub(1))
.await?;
Ok(BlockMeta {
height,
hash,
prev_block_hash,
})
}
pub async fn get_last_info(&self) -> Option<BlockMeta> {
if let Some(cache) = &self.reorg_cache {
if let Ok(v) = cache.read_cache(*cache.items().await.last()?).await {
return Some(v.block);
}
}
None
}
}
fn hex_from_electrs_to_hash(bytes: &[u8]) -> ClientResult<Hash256> {
let hex = hex::decode(bytes)?.into_iter().rev().collect_vec();
Ok(Hash256(
hex.try_into().map_err(|_| HashError::VecWrongLength)?,
))
}