electrs_client 0.2.9

A client for electrs
Documentation
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> {
    /// get blcock hash by height and converts it to other endian
    /// (because of cache and rest use different endians and I use here cache endian)
    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
    }
}

// HACK: endiannes fix
// hash in cache and in rest are in different endians
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)?,
    ))
}