use serde::Deserialize;
use std::collections::HashMap;
pub use bitcoin::consensus::{deserialize, serialize};
use bitcoin::hash_types;
use bitcoin::hash_types::TxMerkleNode;
pub use bitcoin::hex::FromHex;
pub use bitcoin::{
absolute, block, transaction, Address, Amount, Block, BlockHash, CompactTarget, FeeRate,
OutPoint, Script, ScriptBuf, ScriptHash, Sequence, Transaction, TxIn, TxOut, Txid, Weight,
Witness, Wtxid,
};
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct Vin {
pub txid: Txid,
pub vout: u32,
pub prevout: Option<Vout>,
pub scriptsig: ScriptBuf,
#[serde(default)]
pub witness: Witness,
pub sequence: Sequence,
pub is_coinbase: bool,
}
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct Vout {
#[serde(with = "bitcoin::amount::serde::as_sat")]
pub value: Amount,
pub scriptpubkey: ScriptBuf,
}
#[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, PartialEq, Eq)]
pub struct EsploraTx {
pub txid: Txid,
pub version: transaction::Version,
pub locktime: absolute::LockTime,
pub vin: Vec<Vin>,
pub vout: Vec<Vout>,
pub size: usize,
pub weight: Weight,
pub status: TxStatus,
#[serde(with = "bitcoin::amount::serde::as_sat")]
pub fee: Amount,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BlockInfo {
pub id: BlockHash,
pub height: u32,
pub version: block::Version,
pub timestamp: u64,
pub tx_count: u64,
pub size: usize,
pub weight: Weight,
pub merkle_root: hash_types::TxMerkleNode,
pub previousblockhash: Option<BlockHash>,
pub mediantime: u64,
pub nonce: u32,
pub bits: CompactTarget,
pub difficulty: f64,
}
impl PartialEq for BlockInfo {
fn eq(&self, other: &Self) -> bool {
let Self { difficulty: d1, .. } = self;
let Self { difficulty: d2, .. } = other;
self.id == other.id
&& self.height == other.height
&& self.version == other.version
&& self.timestamp == other.timestamp
&& self.tx_count == other.tx_count
&& self.size == other.size
&& self.weight == other.weight
&& self.merkle_root == other.merkle_root
&& self.previousblockhash == other.previousblockhash
&& self.mediantime == other.mediantime
&& self.nonce == other.nonce
&& self.bits == other.bits
&& ((d1.is_nan() && d2.is_nan()) || (d1 == d2))
}
}
impl Eq for BlockInfo {}
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct BlockTime {
pub timestamp: u64,
pub height: u32,
}
#[allow(deprecated)]
#[deprecated(since = "0.13.0", note = "use `BlockInfo` instead")]
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct BlockSummary {
pub id: BlockHash,
#[serde(flatten)]
pub time: BlockTime,
pub previousblockhash: Option<BlockHash>,
pub merkle_root: TxMerkleNode,
}
#[derive(Debug, Clone, Deserialize, PartialEq, Eq)]
pub struct AddressStats {
#[serde(deserialize_with = "deserialize_address_assume_checked")]
pub address: Address,
pub chain_stats: AddressTxsSummary,
pub mempool_stats: AddressTxsSummary,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
pub struct AddressTxsSummary {
pub funded_txo_count: u32,
#[serde(with = "bitcoin::amount::serde::as_sat")]
pub funded_txo_sum: Amount,
pub spent_txo_count: u32,
#[serde(with = "bitcoin::amount::serde::as_sat")]
pub spent_txo_sum: Amount,
pub tx_count: u32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
pub struct ScriptHashStats {
pub chain_stats: ScriptHashTxsSummary,
pub mempool_stats: ScriptHashTxsSummary,
}
pub type ScriptHashTxsSummary = AddressTxsSummary;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
pub struct UtxoStatus {
pub confirmed: bool,
pub block_height: Option<u32>,
pub block_hash: Option<BlockHash>,
pub block_time: Option<u64>,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
pub struct Utxo {
pub txid: Txid,
pub vout: u32,
pub status: UtxoStatus,
#[serde(with = "bitcoin::amount::serde::as_sat")]
pub value: Amount,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct MempoolStats {
pub count: usize,
pub vsize: usize,
#[serde(with = "bitcoin::amount::serde::as_sat")]
pub total_fee: Amount,
#[serde(deserialize_with = "deserialize_fee_histogram")]
pub fee_histogram: Vec<(FeeRate, Weight)>,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct MempoolRecentTx {
pub txid: Txid,
#[serde(with = "bitcoin::amount::serde::as_sat")]
pub fee: Amount,
pub vsize: usize,
#[serde(with = "bitcoin::amount::serde::as_sat")]
pub value: Amount,
}
#[derive(Deserialize, Debug)]
pub struct SubmitPackageResult {
pub package_msg: String,
#[serde(rename = "tx-results")]
pub tx_results: HashMap<Wtxid, TxResult>,
#[serde(rename = "replaced-transactions")]
pub replaced_transactions: Option<Vec<Txid>>,
}
#[derive(Deserialize, Debug)]
pub struct TxResult {
pub txid: Txid,
#[serde(rename = "other-wtxid")]
pub other_wtxid: Option<Wtxid>,
pub vsize: Option<u32>,
pub fees: Option<MempoolFeesSubmitPackage>,
pub error: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct MempoolFeesSubmitPackage {
#[serde(with = "bitcoin::amount::serde::as_btc")]
pub base: Amount,
#[serde(
rename = "effective-feerate",
default,
deserialize_with = "deserialize_feerate"
)]
pub effective_feerate: Option<FeeRate>,
#[serde(rename = "effective-includes")]
pub effective_includes: Option<Vec<Wtxid>>,
}
impl EsploraTx {
pub fn to_tx(&self) -> Transaction {
Transaction {
version: self.version,
lock_time: self.locktime,
input: self
.vin
.iter()
.cloned()
.map(|vin| TxIn {
previous_output: OutPoint {
txid: vin.txid,
vout: vin.vout,
},
script_sig: vin.scriptsig,
sequence: vin.sequence,
witness: vin.witness,
})
.collect(),
output: self
.vout
.iter()
.cloned()
.map(|vout| TxOut {
value: vout.value,
script_pubkey: vout.scriptpubkey,
})
.collect(),
}
}
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(|prevout| TxOut {
script_pubkey: prevout.scriptpubkey,
value: prevout.value,
})
})
.collect()
}
}
impl From<EsploraTx> for Transaction {
fn from(tx: EsploraTx) -> Self {
tx.to_tx()
}
}
impl From<&EsploraTx> for Transaction {
fn from(tx: &EsploraTx) -> Self {
tx.to_tx()
}
}
fn deserialize_address_assume_checked<'de, D>(d: D) -> Result<Address, D::Error>
where
D: serde::de::Deserializer<'de>,
{
let address = Address::<bitcoin::address::NetworkUnchecked>::deserialize(d)?;
Ok(address.assume_checked())
}
fn deserialize_feerate<'de, D>(d: D) -> Result<Option<FeeRate>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use serde::de::Error;
let btc_per_kvb = match Option::<f64>::deserialize(d)? {
Some(v) => v,
None => return Ok(None),
};
let sat_per_kwu = btc_per_kvb * 25_000_000.0;
if sat_per_kwu.is_infinite() {
return Err(D::Error::custom("feerate overflow"));
}
Ok(Some(FeeRate::from_sat_per_kwu(sat_per_kwu as u64)))
}
fn deserialize_fee_histogram<'de, D>(d: D) -> Result<Vec<(FeeRate, Weight)>, D::Error>
where
D: serde::de::Deserializer<'de>,
{
use serde::de::Error;
let raw = Vec::<(f64, Weight)>::deserialize(d)?;
raw.into_iter()
.map(|(sat_per_vb, vsize)| {
let sat_per_kwu = sat_per_vb * 250.0;
if !sat_per_kwu.is_finite() {
return Err(D::Error::custom("feerate overflow"));
}
Ok((FeeRate::from_sat_per_kwu(sat_per_kwu as u64), vsize))
})
.collect()
}