use std::io::{Read, Write};
use coins_core::{
ser::{ByteFormat, SerError, SerResult},
types::tx::Output,
};
use crate::types::script::{ScriptPubkey, ScriptType};
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq)]
pub struct TxOut {
pub value: u64,
pub script_pubkey: ScriptPubkey,
}
impl Output for TxOut {
type Value = u64;
type RecipientIdentifier = ScriptPubkey;
}
impl Default for TxOut {
fn default() -> Self {
Self::null()
}
}
impl TxOut {
pub fn new<T>(value: u64, script_pubkey: T) -> Self
where
T: Into<ScriptPubkey>,
{
TxOut {
value,
script_pubkey: script_pubkey.into(),
}
}
pub fn null() -> Self {
TxOut {
value: 0xffff_ffff_ffff_ffff,
script_pubkey: ScriptPubkey::null(),
}
}
pub fn op_return(data: &[u8]) -> Self {
let mut data = data.to_vec();
data.truncate(75);
let mut payload = vec![0x6a, data.len() as u8];
payload.extend(data);
TxOut {
value: 0,
script_pubkey: ScriptPubkey::from(payload),
}
}
pub fn standard_type(&self) -> ScriptType {
self.script_pubkey.standard_type()
}
pub fn extract_op_return_data(&self) -> Option<Vec<u8>> {
self.script_pubkey.extract_op_return_data()
}
}
impl ByteFormat for TxOut {
type Error = SerError;
fn serialized_length(&self) -> usize {
let mut len = 8; len += self.script_pubkey.serialized_length();
len
}
fn read_from<R>(reader: &mut R) -> SerResult<Self>
where
R: Read,
Self: std::marker::Sized,
{
let value = coins_core::ser::read_u64_le(reader)?;
Ok(TxOut {
value,
script_pubkey: ScriptPubkey::read_from(reader)?,
})
}
fn write_to<W>(&self, writer: &mut W) -> SerResult<usize>
where
W: Write,
{
let mut len = coins_core::ser::write_u64_le(writer, self.value)?;
len += self.script_pubkey.write_to(writer)?;
Ok(len)
}
}
pub type Vout = Vec<TxOut>;
#[cfg(test)]
mod test {
use super::*;
use coins_core::ser::ByteFormat;
#[test]
fn it_serializes_and_derializes_outputs() {
let cases = [
(TxOut::new(0, vec![]), "000000000000000000", 9),
(TxOut::null(), "ffffffffffffffff00", 9),
];
for case in cases.iter() {
assert_eq!(case.0.serialized_length(), case.2);
assert_eq!(case.0.serialize_hex(), case.1);
assert_eq!(TxOut::deserialize_hex(case.1).unwrap(), case.0);
}
}
}