use sha2::{Sha256, Digest};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Transaction {
pub version: i32,
pub inputs: Vec<TxInput>,
pub outputs: Vec<TxOutput>,
pub locktime: u32,
}
impl Default for Transaction {
fn default() -> Self {
Self {
version: 2,
inputs: Vec::new(),
outputs: Vec::new(),
locktime: 0,
}
}
}
impl Transaction {
pub fn new() -> Self {
Self::default()
}
pub fn is_segwit(&self) -> bool {
self.inputs.iter().any(|input| !input.witness.is_empty())
}
pub fn txid(&self) -> [u8; 32] {
let bytes = self.serialize_legacy();
let hash1 = Sha256::digest(&bytes);
let hash2 = Sha256::digest(hash1);
let mut txid: [u8; 32] = hash2.into();
txid.reverse(); txid
}
pub fn wtxid(&self) -> [u8; 32] {
if !self.is_segwit() {
return self.txid();
}
let bytes = self.serialize();
let hash1 = Sha256::digest(&bytes);
let hash2 = Sha256::digest(hash1);
let mut wtxid: [u8; 32] = hash2.into();
wtxid.reverse();
wtxid
}
pub fn serialize(&self) -> Vec<u8> {
if self.is_segwit() {
self.serialize_segwit()
} else {
self.serialize_legacy()
}
}
pub fn serialize_legacy(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&self.version.to_le_bytes());
bytes.extend_from_slice(&encode_varint(self.inputs.len() as u64));
for input in &self.inputs {
bytes.extend_from_slice(&input.txid);
bytes.extend_from_slice(&input.vout.to_le_bytes());
bytes.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
bytes.extend_from_slice(&input.script_sig);
bytes.extend_from_slice(&input.sequence.to_le_bytes());
}
bytes.extend_from_slice(&encode_varint(self.outputs.len() as u64));
for output in &self.outputs {
bytes.extend_from_slice(&output.value.to_le_bytes());
bytes.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
bytes.extend_from_slice(&output.script_pubkey);
}
bytes.extend_from_slice(&self.locktime.to_le_bytes());
bytes
}
fn serialize_segwit(&self) -> Vec<u8> {
let mut bytes = Vec::new();
bytes.extend_from_slice(&self.version.to_le_bytes());
bytes.push(0x00); bytes.push(0x01);
bytes.extend_from_slice(&encode_varint(self.inputs.len() as u64));
for input in &self.inputs {
bytes.extend_from_slice(&input.txid);
bytes.extend_from_slice(&input.vout.to_le_bytes());
bytes.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
bytes.extend_from_slice(&input.script_sig);
bytes.extend_from_slice(&input.sequence.to_le_bytes());
}
bytes.extend_from_slice(&encode_varint(self.outputs.len() as u64));
for output in &self.outputs {
bytes.extend_from_slice(&output.value.to_le_bytes());
bytes.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
bytes.extend_from_slice(&output.script_pubkey);
}
for input in &self.inputs {
bytes.extend_from_slice(&encode_varint(input.witness.len() as u64));
for item in &input.witness {
bytes.extend_from_slice(&encode_varint(item.len() as u64));
bytes.extend_from_slice(item);
}
}
bytes.extend_from_slice(&self.locktime.to_le_bytes());
bytes
}
pub fn size(&self) -> usize {
self.serialize().len()
}
pub fn weight(&self) -> usize {
let base_size = self.serialize_legacy().len();
let total_size = self.serialize().len();
base_size * 3 + total_size
}
pub fn vsize(&self) -> usize {
self.weight().div_ceil(4)
}
pub fn to_hex(&self) -> String {
self.serialize()
.iter()
.map(|b| format!("{:02x}", b))
.collect()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TxInput {
pub txid: [u8; 32],
pub vout: u32,
pub script_sig: Vec<u8>,
pub sequence: u32,
pub witness: Vec<Vec<u8>>,
}
impl TxInput {
pub fn new(txid: [u8; 32], vout: u32) -> Self {
Self {
txid,
vout,
script_sig: Vec::new(),
sequence: 0xFFFFFFFF,
witness: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TxOutput {
pub value: u64,
pub script_pubkey: Vec<u8>,
}
impl TxOutput {
pub fn new(value: u64, script_pubkey: Vec<u8>) -> Self {
Self { value, script_pubkey }
}
}
#[derive(Debug, Clone)]
pub struct Utxo {
pub txid: [u8; 32],
pub vout: u32,
pub value: u64,
pub script_pubkey: Vec<u8>,
pub address: String,
}
pub fn encode_varint(n: u64) -> Vec<u8> {
if n < 0xFD {
vec![n as u8]
} else if n <= 0xFFFF {
let mut v = vec![0xFD];
v.extend_from_slice(&(n as u16).to_le_bytes());
v
} else if n <= 0xFFFFFFFF {
let mut v = vec![0xFE];
v.extend_from_slice(&(n as u32).to_le_bytes());
v
} else {
let mut v = vec![0xFF];
v.extend_from_slice(&n.to_le_bytes());
v
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_encode_varint() {
assert_eq!(encode_varint(0), vec![0x00]);
assert_eq!(encode_varint(252), vec![0xFC]);
assert_eq!(encode_varint(253), vec![0xFD, 0xFD, 0x00]);
assert_eq!(encode_varint(0xFFFF), vec![0xFD, 0xFF, 0xFF]);
}
#[test]
fn test_empty_transaction() {
let tx = Transaction::new();
assert_eq!(tx.version, 2);
assert!(tx.inputs.is_empty());
assert!(tx.outputs.is_empty());
assert!(!tx.is_segwit());
}
}