use crate::{TxOut, Txid, Vout};
use bitcoin::ScriptBuf;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize, Serializer, ser::SerializeStruct};
#[derive(Debug, Clone, Deserialize, JsonSchema)]
pub struct TxIn {
#[schemars(example = "0000000000000000000000000000000000000000000000000000000000000000")]
pub txid: Txid,
#[schemars(example = 0)]
pub vout: Vout,
#[schemars(example = None as Option<TxOut>)]
pub prevout: Option<TxOut>,
#[schemars(rename = "scriptsig", with = "String")]
pub script_sig: ScriptBuf,
#[schemars(rename = "scriptsig_asm", with = "String")]
pub script_sig_asm: (),
pub witness: Vec<String>,
#[schemars(example = false)]
pub is_coinbase: bool,
#[schemars(example = 4294967293_u32)]
pub sequence: u32,
#[schemars(rename = "inner_redeemscript_asm", with = "String")]
pub inner_redeem_script_asm: (),
#[schemars(rename = "inner_witnessscript_asm", with = "String")]
pub inner_witness_script_asm: (),
}
impl Serialize for TxIn {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let has_witness = !self.witness.is_empty();
let has_scriptsig = !self.script_sig.is_empty();
let is_p2sh = self
.prevout
.as_ref()
.is_some_and(|p| p.script_pubkey.is_p2sh());
let inner_redeem = if has_scriptsig && is_p2sh && !self.is_coinbase {
self.script_sig
.redeem_script()
.map(|s| s.to_asm_string())
.unwrap_or_default()
} else {
String::new()
};
let is_p2tr = self
.prevout
.as_ref()
.is_some_and(|p| p.script_pubkey.is_p2tr());
let inner_witness = if has_witness && self.witness.len() > 2 {
let script_hex = if is_p2tr {
self.witness.get(self.witness.len() - 2)
} else {
self.witness.last()
};
if let Some(hex) = script_hex {
let bytes: Vec<u8> = bitcoin::hex::FromHex::from_hex(hex).unwrap_or_default();
ScriptBuf::from(bytes).to_asm_string()
} else {
String::new()
}
} else {
String::new()
};
let has_inner_redeem = is_p2sh && !self.is_coinbase;
let has_inner_witness = !inner_witness.is_empty();
let field_count =
7 + has_witness as usize + has_inner_redeem as usize + has_inner_witness as usize;
let mut state = serializer.serialize_struct("TxIn", field_count)?;
state.serialize_field("txid", &self.txid)?;
state.serialize_field("vout", &self.vout)?;
state.serialize_field("prevout", &self.prevout)?;
state.serialize_field("scriptsig", &self.script_sig.to_hex_string())?;
state.serialize_field("scriptsig_asm", &self.script_sig.to_asm_string())?;
if has_witness {
state.serialize_field("witness", &self.witness)?;
}
state.serialize_field("is_coinbase", &self.is_coinbase)?;
state.serialize_field("sequence", &self.sequence)?;
if has_inner_redeem {
state.serialize_field("inner_redeemscript_asm", &inner_redeem)?;
}
if has_inner_witness {
state.serialize_field("inner_witnessscript_asm", &inner_witness)?;
}
state.end()
}
}