rustywallet_tx/
types.rs

1//! Core transaction types.
2
3use sha2::{Sha256, Digest};
4
5/// A Bitcoin transaction.
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct Transaction {
8    /// Transaction version (typically 1 or 2)
9    pub version: i32,
10    /// Transaction inputs
11    pub inputs: Vec<TxInput>,
12    /// Transaction outputs
13    pub outputs: Vec<TxOutput>,
14    /// Lock time (block height or timestamp)
15    pub locktime: u32,
16}
17
18impl Default for Transaction {
19    fn default() -> Self {
20        Self {
21            version: 2,
22            inputs: Vec::new(),
23            outputs: Vec::new(),
24            locktime: 0,
25        }
26    }
27}
28
29impl Transaction {
30    /// Create a new empty transaction.
31    pub fn new() -> Self {
32        Self::default()
33    }
34
35    /// Check if this is a SegWit transaction (has witness data).
36    pub fn is_segwit(&self) -> bool {
37        self.inputs.iter().any(|input| !input.witness.is_empty())
38    }
39
40    /// Calculate the transaction ID (double SHA256 of non-witness serialization).
41    pub fn txid(&self) -> [u8; 32] {
42        let bytes = self.serialize_legacy();
43        let hash1 = Sha256::digest(&bytes);
44        let hash2 = Sha256::digest(hash1);
45        let mut txid: [u8; 32] = hash2.into();
46        txid.reverse(); // Bitcoin uses little-endian for display
47        txid
48    }
49
50    /// Calculate the witness transaction ID.
51    pub fn wtxid(&self) -> [u8; 32] {
52        if !self.is_segwit() {
53            return self.txid();
54        }
55        let bytes = self.serialize();
56        let hash1 = Sha256::digest(&bytes);
57        let hash2 = Sha256::digest(hash1);
58        let mut wtxid: [u8; 32] = hash2.into();
59        wtxid.reverse();
60        wtxid
61    }
62
63    /// Serialize transaction (with witness if present).
64    pub fn serialize(&self) -> Vec<u8> {
65        if self.is_segwit() {
66            self.serialize_segwit()
67        } else {
68            self.serialize_legacy()
69        }
70    }
71
72    /// Serialize without witness data (for txid calculation).
73    pub fn serialize_legacy(&self) -> Vec<u8> {
74        let mut bytes = Vec::new();
75        
76        // Version (4 bytes, little-endian)
77        bytes.extend_from_slice(&self.version.to_le_bytes());
78        
79        // Input count (varint)
80        bytes.extend_from_slice(&encode_varint(self.inputs.len() as u64));
81        
82        // Inputs
83        for input in &self.inputs {
84            bytes.extend_from_slice(&input.txid);
85            bytes.extend_from_slice(&input.vout.to_le_bytes());
86            bytes.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
87            bytes.extend_from_slice(&input.script_sig);
88            bytes.extend_from_slice(&input.sequence.to_le_bytes());
89        }
90        
91        // Output count (varint)
92        bytes.extend_from_slice(&encode_varint(self.outputs.len() as u64));
93        
94        // Outputs
95        for output in &self.outputs {
96            bytes.extend_from_slice(&output.value.to_le_bytes());
97            bytes.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
98            bytes.extend_from_slice(&output.script_pubkey);
99        }
100        
101        // Locktime (4 bytes, little-endian)
102        bytes.extend_from_slice(&self.locktime.to_le_bytes());
103        
104        bytes
105    }
106
107    /// Serialize with witness data.
108    fn serialize_segwit(&self) -> Vec<u8> {
109        let mut bytes = Vec::new();
110        
111        // Version
112        bytes.extend_from_slice(&self.version.to_le_bytes());
113        
114        // Marker and flag for SegWit
115        bytes.push(0x00); // marker
116        bytes.push(0x01); // flag
117        
118        // Input count
119        bytes.extend_from_slice(&encode_varint(self.inputs.len() as u64));
120        
121        // Inputs (without witness)
122        for input in &self.inputs {
123            bytes.extend_from_slice(&input.txid);
124            bytes.extend_from_slice(&input.vout.to_le_bytes());
125            bytes.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
126            bytes.extend_from_slice(&input.script_sig);
127            bytes.extend_from_slice(&input.sequence.to_le_bytes());
128        }
129        
130        // Output count
131        bytes.extend_from_slice(&encode_varint(self.outputs.len() as u64));
132        
133        // Outputs
134        for output in &self.outputs {
135            bytes.extend_from_slice(&output.value.to_le_bytes());
136            bytes.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
137            bytes.extend_from_slice(&output.script_pubkey);
138        }
139        
140        // Witness data
141        for input in &self.inputs {
142            bytes.extend_from_slice(&encode_varint(input.witness.len() as u64));
143            for item in &input.witness {
144                bytes.extend_from_slice(&encode_varint(item.len() as u64));
145                bytes.extend_from_slice(item);
146            }
147        }
148        
149        // Locktime
150        bytes.extend_from_slice(&self.locktime.to_le_bytes());
151        
152        bytes
153    }
154
155    /// Get transaction size in bytes.
156    pub fn size(&self) -> usize {
157        self.serialize().len()
158    }
159
160    /// Get transaction weight (for fee calculation).
161    pub fn weight(&self) -> usize {
162        let base_size = self.serialize_legacy().len();
163        let total_size = self.serialize().len();
164        base_size * 3 + total_size
165    }
166
167    /// Get virtual size (vsize) for fee calculation.
168    pub fn vsize(&self) -> usize {
169        self.weight().div_ceil(4)
170    }
171
172    /// Convert to hex string.
173    pub fn to_hex(&self) -> String {
174        self.serialize()
175            .iter()
176            .map(|b| format!("{:02x}", b))
177            .collect()
178    }
179}
180
181/// A transaction input.
182#[derive(Debug, Clone, PartialEq, Eq)]
183pub struct TxInput {
184    /// Previous transaction ID (32 bytes, internal byte order)
185    pub txid: [u8; 32],
186    /// Output index in previous transaction
187    pub vout: u32,
188    /// Unlocking script (scriptSig)
189    pub script_sig: Vec<u8>,
190    /// Sequence number
191    pub sequence: u32,
192    /// Witness data (for SegWit)
193    pub witness: Vec<Vec<u8>>,
194}
195
196impl TxInput {
197    /// Create a new input.
198    pub fn new(txid: [u8; 32], vout: u32) -> Self {
199        Self {
200            txid,
201            vout,
202            script_sig: Vec::new(),
203            sequence: 0xFFFFFFFF,
204            witness: Vec::new(),
205        }
206    }
207}
208
209/// A transaction output.
210#[derive(Debug, Clone, PartialEq, Eq)]
211pub struct TxOutput {
212    /// Value in satoshis
213    pub value: u64,
214    /// Locking script (scriptPubKey)
215    pub script_pubkey: Vec<u8>,
216}
217
218impl TxOutput {
219    /// Create a new output.
220    pub fn new(value: u64, script_pubkey: Vec<u8>) -> Self {
221        Self { value, script_pubkey }
222    }
223}
224
225/// An unspent transaction output (UTXO).
226#[derive(Debug, Clone)]
227pub struct Utxo {
228    /// Transaction ID
229    pub txid: [u8; 32],
230    /// Output index
231    pub vout: u32,
232    /// Value in satoshis
233    pub value: u64,
234    /// Script pubkey
235    pub script_pubkey: Vec<u8>,
236    /// Address (for display)
237    pub address: String,
238}
239
240/// Encode a variable-length integer.
241pub fn encode_varint(n: u64) -> Vec<u8> {
242    if n < 0xFD {
243        vec![n as u8]
244    } else if n <= 0xFFFF {
245        let mut v = vec![0xFD];
246        v.extend_from_slice(&(n as u16).to_le_bytes());
247        v
248    } else if n <= 0xFFFFFFFF {
249        let mut v = vec![0xFE];
250        v.extend_from_slice(&(n as u32).to_le_bytes());
251        v
252    } else {
253        let mut v = vec![0xFF];
254        v.extend_from_slice(&n.to_le_bytes());
255        v
256    }
257}
258
259#[cfg(test)]
260mod tests {
261    use super::*;
262
263    #[test]
264    fn test_encode_varint() {
265        assert_eq!(encode_varint(0), vec![0x00]);
266        assert_eq!(encode_varint(252), vec![0xFC]);
267        assert_eq!(encode_varint(253), vec![0xFD, 0xFD, 0x00]);
268        assert_eq!(encode_varint(0xFFFF), vec![0xFD, 0xFF, 0xFF]);
269    }
270
271    #[test]
272    fn test_empty_transaction() {
273        let tx = Transaction::new();
274        assert_eq!(tx.version, 2);
275        assert!(tx.inputs.is_empty());
276        assert!(tx.outputs.is_empty());
277        assert!(!tx.is_segwit());
278    }
279}