use std::fmt::Debug;
use std::hash::Hash;
use std::str::FromStr;
use bitcoin::blockdata::constants;
use bitcoin::{BlockHash, Network, OutPoint};
use chrono::{DateTime, NaiveDateTime};
#[cfg(feature = "electrum")]
use electrum_client::ListUnspentRes;
use strict_encoding::{StrictDecode, StrictEncode};
#[derive(
Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Display, From, Error
)]
#[display(doc_comments)]
#[from(bitcoin::hashes::hex::Error)]
#[from(chrono::ParseError)]
#[from(std::num::ParseIntError)]
#[from(bitcoin::consensus::encode::Error)]
#[from(bitcoin::util::amount::ParseAmountError)]
#[from(bitcoin::blockdata::transaction::ParseOutPointError)]
pub struct ParseError;
#[derive(Getters, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[derive(StrictEncode, StrictDecode)]
#[display("{block_height}#{block_hash}@{timestamp}")]
pub struct TimeHeight {
timestamp: NaiveDateTime,
block_height: u32,
block_hash: BlockHash,
}
impl Default for TimeHeight {
fn default() -> Self {
TimeHeight {
timestamp: DateTime::from_timestamp_millis(1231006500)
.expect("hardcoded value")
.naive_utc(),
block_height: 0,
block_hash: constants::genesis_block(Network::Bitcoin).block_hash(),
}
}
}
impl FromStr for TimeHeight {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut data = s.split(&['#', '@'][..]);
let me = Self {
timestamp: data.next().ok_or(ParseError)?.parse()?,
block_height: data.next().ok_or(ParseError)?.parse()?,
block_hash: data.next().ok_or(ParseError)?.parse()?,
};
if data.next().is_some() {
Err(ParseError)
} else {
Ok(me)
}
}
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
#[derive(
Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Default, Debug, Display
)]
#[derive(StrictEncode, StrictDecode)]
pub enum MiningStatus {
#[default]
#[display("undefined")]
Undefined,
#[display("unknown_tx")]
UnknownTx,
#[display("mempool")]
Mempool,
#[display(inner)]
Blockchain(u64),
}
#[cfg_attr(
feature = "serde",
derive(Serialize, Deserialize),
serde(crate = "serde_crate")
)]
#[derive(Getters, Clone, Ord, PartialOrd, Eq, PartialEq, Hash, Debug, Display)]
#[derive(StrictEncode, StrictDecode)]
#[display("{amount}@{outpoint}")]
pub struct Utxo {
mined: MiningStatus,
outpoint: OutPoint,
#[cfg_attr(
feature = "serde",
serde(with = "bitcoin::util::amount::serde::as_btc")
)]
amount: bitcoin::Amount,
}
impl FromStr for Utxo {
type Err = ParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut split = s.split('@');
match (split.next(), split.next(), split.next()) {
(Some(amount), Some(outpoint), None) => Ok(Utxo {
mined: MiningStatus::Undefined,
amount: amount.parse()?,
outpoint: outpoint.parse()?,
}),
_ => Err(ParseError),
}
}
}
#[cfg(feature = "electrum")]
impl From<ListUnspentRes> for Utxo {
fn from(res: ListUnspentRes) -> Self {
Utxo {
mined: if res.height == 0 {
MiningStatus::Mempool
} else {
MiningStatus::Blockchain(res.height as u64)
},
outpoint: OutPoint::new(res.tx_hash, res.tx_pos as u32),
amount: bitcoin::Amount::from_sat(res.value),
}
}
}