use bitcoin::hash_types;
use serde::Deserialize;
use std::collections::HashMap;
pub use bitcoin::consensus::{deserialize, serialize};
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, Transaction, TxIn, TxOut, Txid, Weight, Witness,
Wtxid,
};
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct PrevOut {
pub value: u64,
pub scriptpubkey: ScriptBuf,
}
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct Vin {
pub txid: Txid,
pub vout: u32,
pub prevout: Option<PrevOut>,
pub scriptsig: ScriptBuf,
#[serde(deserialize_with = "deserialize_witness", default)]
pub witness: Vec<Vec<u8>>,
pub sequence: u32,
pub is_coinbase: bool,
}
#[derive(Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct Vout {
pub value: u64,
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 Tx {
pub txid: Txid,
pub version: i32,
pub locktime: u32,
pub vin: Vec<Vin>,
pub vout: Vec<Vout>,
pub size: usize,
pub weight: u64,
pub status: TxStatus,
pub fee: u64,
}
#[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: u64,
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,
}
#[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 {
pub address: String,
pub chain_stats: AddressTxsSummary,
pub mempool_stats: AddressTxsSummary,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)]
pub struct AddressTxsSummary {
pub funded_txo_count: u32,
pub funded_txo_sum: u64,
pub spent_txo_count: u32,
pub spent_txo_sum: u64,
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,
pub value: Amount,
}
#[derive(Clone, Debug, PartialEq, Deserialize)]
pub struct MempoolStats {
pub count: usize,
pub vsize: usize,
pub total_fee: u64,
pub fee_histogram: Vec<(f64, usize)>,
}
#[derive(Clone, Debug, PartialEq, Eq, Deserialize)]
pub struct MempoolRecentTx {
pub txid: Txid,
pub fee: u64,
pub vsize: usize,
pub value: u64,
}
#[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 Tx {
pub fn to_tx(&self) -> Transaction {
Transaction {
version: transaction::Version::non_standard(self.version),
lock_time: bitcoin::absolute::LockTime::from_consensus(self.locktime),
input: self
.vin
.iter()
.cloned()
.map(|vin| TxIn {
previous_output: OutPoint {
txid: vin.txid,
vout: vin.vout,
},
script_sig: vin.scriptsig,
sequence: bitcoin::Sequence(vin.sequence),
witness: Witness::from_slice(&vin.witness),
})
.collect(),
output: self
.vout
.iter()
.cloned()
.map(|vout| TxOut {
value: Amount::from_sat(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(|po| TxOut {
script_pubkey: po.scriptpubkey,
value: Amount::from_sat(po.value),
})
})
.collect()
}
pub fn weight(&self) -> Weight {
Weight::from_wu(self.weight)
}
pub fn fee(&self) -> Amount {
Amount::from_sat(self.fee)
}
}
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)
}
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)))
}