bp-esplora 0.10.0

Bitcoin Esplora API client library not using rust-bitcoin. Blocking or async
Documentation
//! structs from the esplora API
//!
//! see: <https://github.com/Blockstream/esplora/blob/master/API.md>

use amplify::confinement::Confined;
use amplify::hex::FromHex;
use amplify::{confinement, Bytes32};
use bpstd::{
    BlockHash, LockTime, Outpoint, ScriptPubkey, SeqNo, SigScript, Tx as Transaction, TxIn, TxOut,
    TxVer, Txid, Witness,
};
use serde::Deserialize;
use serde_with::hex::Hex;

#[serde_as]
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct PrevOut {
    pub value: u64,
    #[serde_as(as = "Hex")]
    pub scriptpubkey: ScriptPubkey,
}

#[serde_as]
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct Vin {
    pub txid: Txid,
    pub vout: u32,
    // None if coinbase
    pub prevout: Option<PrevOut>,
    #[serde_as(as = "Hex")]
    pub scriptsig: SigScript,
    #[serde(deserialize_with = "deserialize_witness", default)]
    pub witness: Vec<Vec<u8>>,
    pub sequence: u32,
    pub is_coinbase: bool,
}

#[serde_as]
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct Vout {
    pub value: u64,
    #[serde_as(as = "Hex")]
    pub scriptpubkey: ScriptPubkey,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct TxStatus {
    pub confirmed: bool,
    pub block_height: Option<u32>,
    pub block_hash: Option<BlockHash>,
    pub block_time: Option<u64>,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct MerkleProof {
    pub block_height: u32,
    pub merkle: Vec<Txid>,
    pub pos: usize,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct OutputStatus {
    pub spent: bool,
    pub txid: Option<Txid>,
    pub vin: Option<u64>,
    pub status: Option<TxStatus>,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct BlockStatus {
    pub in_best_chain: bool,
    pub height: Option<u32>,
    pub next_best: Option<BlockHash>,
}

#[derive(Deserialize, Clone, Debug)]
pub struct Tx {
    pub txid: Txid,
    pub version: i32,
    pub locktime: u32,
    pub vin: Vec<Vin>,
    pub vout: Vec<Vout>,
    pub status: TxStatus,
    pub fee: u64,
    pub size: u32,
    pub weight: u32,
}

#[derive(Deserialize, Clone, Debug)]
pub struct Utxo {
    pub txid: Txid,
    pub vout: Vec<Vout>,
    pub value: u64,
    pub status: TxStatus,
}

#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct BlockTime {
    pub timestamp: u64,
    pub height: u32,
}

#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct BlockSummary {
    pub id: BlockHash,
    #[serde(flatten)]
    pub time: BlockTime,
    /// Hash of the previous block, will be `None` for the genesis block.
    pub previousblockhash: Option<BlockHash>,
    pub merkle_root: Bytes32,
}

impl Tx {
    pub fn to_tx(&self) -> Result<Transaction, confinement::Error> {
        let inputs = self.vin.iter().cloned().map(|vin| TxIn {
            prev_output: Outpoint::new(vin.txid, vin.vout),
            sig_script: vin.scriptsig,
            sequence: SeqNo::from_consensus_u32(vin.sequence),
            witness: Witness::from_consensus_stack(vin.witness),
        });
        let outputs = self.vout.iter().cloned().map(|vout| TxOut {
            value: vout.value.into(),
            script_pubkey: vout.scriptpubkey,
        });
        Ok(Transaction {
            version: TxVer::from_consensus_i32(self.version),
            lock_time: LockTime::from_consensus_u32(self.locktime),
            inputs: Confined::try_from_iter(inputs)?,
            outputs: Confined::try_from_iter(outputs)?,
        })
    }

    pub fn confirmation_time(&self) -> Option<BlockTime> {
        match self.status {
            TxStatus {
                confirmed: true,
                block_height: Some(height),
                block_time: Some(timestamp),
                ..
            } => Some(BlockTime { timestamp, height }),
            _ => None,
        }
    }

    pub fn previous_outputs(&self) -> Vec<Option<TxOut>> {
        self.vin
            .iter()
            .cloned()
            .map(|vin| {
                vin.prevout.map(|po| TxOut {
                    script_pubkey: po.scriptpubkey,
                    value: po.value.into(),
                })
            })
            .collect()
    }
}

fn deserialize_witness<'de, D>(d: D) -> Result<Vec<Vec<u8>>, D::Error>
where
    D: serde::de::Deserializer<'de>,
{
    let list = Vec::<String>::deserialize(d)?;
    list.into_iter()
        .map(|hex_str| Vec::<u8>::from_hex(&hex_str))
        .collect::<Result<Vec<Vec<u8>>, _>>()
        .map_err(serde::de::Error::custom)
}