use bitcoin::{OutPoint, Script};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use sidestr_core::block::HeaderFamily;
use sidestr_core::rules::Params;
use sidestr_core::state::{CoinRef, StateOf};
use crate::error::Result;
pub fn coinbase_maturity() -> u32 {
Params::default().coinbase_maturity
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Coin {
#[serde(serialize_with = "ser_outpoint", deserialize_with = "de_outpoint")]
pub outpoint: OutPoint,
pub value: u64,
pub height: u32,
pub coinbase: bool,
}
fn ser_outpoint<S: Serializer>(op: &OutPoint, s: S) -> core::result::Result<S::Ok, S::Error> {
s.serialize_str(&op.to_string())
}
fn de_outpoint<'de, D: Deserializer<'de>>(d: D) -> core::result::Result<OutPoint, D::Error> {
let text = String::deserialize(d)?;
text.parse().map_err(serde::de::Error::custom)
}
impl From<CoinRef> for Coin {
fn from(c: CoinRef) -> Self {
Self {
outpoint: c.outpoint,
value: c.value,
height: c.height,
coinbase: c.coinbase,
}
}
}
impl Coin {
pub fn is_mature(&self, tip_height: u32) -> bool {
!self.coinbase || (tip_height + 1).saturating_sub(self.height) >= coinbase_maturity()
}
}
pub fn from_json(text: &str) -> Result<Vec<Coin>> {
Ok(serde_json::from_str(text)?)
}
pub fn from_state<F: HeaderFamily>(state: &StateOf<F>, script: &Script) -> Vec<Coin> {
state.coins(script).into_iter().map(Coin::from).collect()
}
pub fn mature(coins: &[Coin], tip_height: u32) -> Vec<Coin> {
coins
.iter()
.filter(|c| c.is_mature(tip_height))
.cloned()
.collect()
}
pub fn balance(coins: &[Coin]) -> u64 {
coins.iter().map(|c| c.value).sum()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn json_shape_is_the_producers() {
let bad = from_json(r#"[{"outpoint":"nothex:0","value":1,"height":0,"coinbase":false}]"#);
assert!(bad.is_err());
let c = from_json(&format!(
r#"[{{"outpoint":"{}:7","value":1,"height":0,"coinbase":false}}]"#,
"ab".repeat(32)
))
.unwrap();
assert_eq!(c[0].outpoint.vout, 7);
assert_eq!(c[0].outpoint.txid.to_string(), "ab".repeat(32));
assert!(c[0].is_mature(0));
assert_eq!(coinbase_maturity(), 100);
}
}