1use super::varint::{decode_varint, encode_varint};
7use crate::error::{ConsensusError, Result};
8use crate::types::*;
9use std::borrow::Cow;
10
11#[cfg(feature = "production")]
12use smallvec::SmallVec;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum TransactionParseError {
17 InsufficientBytes,
18 InvalidVersion,
19 InvalidInputCount,
20 InvalidOutputCount,
21 InvalidScriptLength,
22 InvalidLockTime,
23}
24
25impl std::fmt::Display for TransactionParseError {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self {
28 TransactionParseError::InsufficientBytes => {
29 write!(f, "Insufficient bytes to parse transaction")
30 }
31 TransactionParseError::InvalidVersion => write!(f, "Invalid transaction version"),
32 TransactionParseError::InvalidInputCount => write!(f, "Invalid input count"),
33 TransactionParseError::InvalidOutputCount => write!(f, "Invalid output count"),
34 TransactionParseError::InvalidScriptLength => write!(f, "Invalid script length"),
35 TransactionParseError::InvalidLockTime => write!(f, "Invalid lock time"),
36 }
37 }
38}
39
40impl std::error::Error for TransactionParseError {}
41
42#[inline]
43fn checked_slice_end(offset: usize, len: u64) -> Result<usize> {
44 let len = usize::try_from(len).map_err(|_| {
45 ConsensusError::Serialization(Cow::Owned(
46 TransactionParseError::InvalidScriptLength.to_string(),
47 ))
48 })?;
49 offset.checked_add(len).ok_or_else(|| {
50 ConsensusError::Serialization(Cow::Owned(
51 TransactionParseError::InsufficientBytes.to_string(),
52 ))
53 })
54}
55
56fn read_tx_input_count_after_version(
67 data: &[u8],
68 mut offset: usize,
69) -> Result<(bool, u64, usize, bool)> {
70 let (mut input_count, varint_len) = decode_varint(&data[offset..])?;
71 offset += varint_len;
72
73 if input_count > 1_000_000 {
74 return Err(ConsensusError::Serialization(Cow::Owned(
75 TransactionParseError::InvalidInputCount.to_string(),
76 )));
77 }
78
79 let mut is_segwit = false;
80 let mut implicit_empty_outputs = false;
82
83 if input_count == 0 {
84 if offset >= data.len() {
85 return Err(ConsensusError::Serialization(Cow::Owned(
86 TransactionParseError::InsufficientBytes.to_string(),
87 )));
88 }
89 let flag = data[offset];
90 offset += 1;
91
92 if flag == 0 {
93 implicit_empty_outputs = true;
94 return Ok((false, 0, offset, implicit_empty_outputs));
95 }
96
97 if flag != 1 {
98 return Err(ConsensusError::Serialization(Cow::Owned(format!(
99 "Unsupported segwit transaction flag: {flag}"
100 ))));
101 }
102
103 is_segwit = true;
104
105 let (ic2, vl2) = decode_varint(&data[offset..])?;
106 offset += vl2;
107 if ic2 > 1_000_000 {
108 return Err(ConsensusError::Serialization(Cow::Owned(
109 TransactionParseError::InvalidInputCount.to_string(),
110 )));
111 }
112 input_count = ic2;
113 }
114
115 Ok((is_segwit, input_count, offset, implicit_empty_outputs))
116}
117
118#[inline(always)]
120pub fn serialize_transaction(tx: &Transaction) -> Vec<u8> {
121 let mut result = Vec::new();
122 serialize_transaction_append(&mut result, tx);
123 result
124}
125
126#[inline(always)]
134fn serialize_transaction_append(result: &mut Vec<u8>, tx: &Transaction) {
135 result.extend_from_slice(&(tx.version as u32).to_le_bytes());
136
137 if tx.inputs.is_empty() && !tx.outputs.is_empty() {
138 result.extend_from_slice(&encode_varint(0));
139 result.push(0x01);
140 result.extend_from_slice(&encode_varint(tx.inputs.len() as u64));
141 } else {
142 result.extend_from_slice(&encode_varint(tx.inputs.len() as u64));
143 }
144
145 for input in &tx.inputs {
146 result.extend_from_slice(&input.prevout.hash);
147 result.extend_from_slice(&input.prevout.index.to_le_bytes());
148 result.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
149 result.extend_from_slice(&input.script_sig);
150 result.extend_from_slice(&(input.sequence as u32).to_le_bytes());
151 }
152
153 result.extend_from_slice(&encode_varint(tx.outputs.len() as u64));
154
155 for output in &tx.outputs {
156 result.extend_from_slice(&(output.value as u64).to_le_bytes());
157 result.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
158 result.extend_from_slice(&output.script_pubkey);
159 }
160
161 result.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
162}
163
164#[inline(always)]
166pub fn serialize_transaction_into(dst: &mut Vec<u8>, tx: &Transaction) -> usize {
167 dst.clear();
168 serialize_transaction_append(dst, tx);
169 dst.len()
170}
171
172pub fn serialize_transaction_with_witness(tx: &Transaction, witnesses: &[Witness]) -> Vec<u8> {
174 assert_eq!(
175 witnesses.len(),
176 tx.inputs.len(),
177 "witness count must match input count"
178 );
179 let mut result = Vec::new();
180 result.extend_from_slice(&(tx.version as u32).to_le_bytes());
181 result.push(0x00);
182 result.push(0x01);
183 result.extend_from_slice(&encode_varint(tx.inputs.len() as u64));
184 for input in &tx.inputs {
185 result.extend_from_slice(&input.prevout.hash);
186 result.extend_from_slice(&input.prevout.index.to_le_bytes());
187 result.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
188 result.extend_from_slice(&input.script_sig);
189 result.extend_from_slice(&(input.sequence as u32).to_le_bytes());
190 }
191 result.extend_from_slice(&encode_varint(tx.outputs.len() as u64));
192 for output in &tx.outputs {
193 result.extend_from_slice(&(output.value as u64).to_le_bytes());
194 result.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
195 result.extend_from_slice(&output.script_pubkey);
196 }
197 for witness in witnesses {
198 result.extend_from_slice(&encode_varint(witness.len() as u64));
199 for element in witness {
200 result.extend_from_slice(&encode_varint(element.len() as u64));
201 result.extend_from_slice(element);
202 }
203 }
204 result.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
205 result
206}
207
208pub fn deserialize_transaction(data: &[u8]) -> Result<Transaction> {
213 deserialize_transaction_with_witness(data).map(|(tx, _, _)| tx)
214}
215
216#[cfg(feature = "production")]
222type TxInputVec = SmallVec<[TransactionInput; 2]>;
223#[cfg(not(feature = "production"))]
224type TxInputVec = Vec<TransactionInput>;
225
226#[cfg(feature = "production")]
227type TxOutputVec = SmallVec<[TransactionOutput; 2]>;
228#[cfg(not(feature = "production"))]
229type TxOutputVec = Vec<TransactionOutput>;
230
231#[inline]
233fn parse_inputs(data: &[u8], offset: &mut usize, count: u64) -> Result<TxInputVec> {
234 let mut inputs = TxInputVec::new();
235
236 for _ in 0..count {
237 if data.len() < *offset + 36 {
238 return Err(ConsensusError::Serialization(Cow::Owned(
239 TransactionParseError::InsufficientBytes.to_string(),
240 )));
241 }
242 let mut hash = [0u8; 32];
243 hash.copy_from_slice(&data[*offset..*offset + 32]);
244 *offset += 32;
245
246 let index = u32::from_le_bytes([
247 data[*offset],
248 data[*offset + 1],
249 data[*offset + 2],
250 data[*offset + 3],
251 ]);
252 *offset += 4;
253
254 let (script_len, varint_len) = decode_varint(&data[*offset..])?;
255 *offset += varint_len;
256
257 let script_sig_end = checked_slice_end(*offset, script_len)?;
258 if data.len() < script_sig_end {
259 return Err(ConsensusError::Serialization(Cow::Owned(
260 TransactionParseError::InsufficientBytes.to_string(),
261 )));
262 }
263 let script_sig = data[*offset..script_sig_end].to_vec();
264 *offset = script_sig_end;
265
266 if data.len() < *offset + 4 {
267 return Err(ConsensusError::Serialization(Cow::Owned(
268 TransactionParseError::InsufficientBytes.to_string(),
269 )));
270 }
271 let sequence = u32::from_le_bytes([
272 data[*offset],
273 data[*offset + 1],
274 data[*offset + 2],
275 data[*offset + 3],
276 ]) as u64;
277 *offset += 4;
278
279 inputs.push(TransactionInput {
280 prevout: OutPoint { hash, index },
281 script_sig,
282 sequence,
283 });
284 }
285
286 Ok(inputs)
287}
288
289#[inline]
292fn parse_outputs(
293 data: &[u8],
294 offset: &mut usize,
295 implicit_empty_outputs: bool,
296) -> Result<TxOutputVec> {
297 let output_count = if implicit_empty_outputs {
298 0
299 } else {
300 let (output_count, varint_len) = decode_varint(&data[*offset..])?;
301 *offset += varint_len;
302
303 if output_count > 1_000_000 {
304 return Err(ConsensusError::Serialization(Cow::Owned(
305 TransactionParseError::InvalidOutputCount.to_string(),
306 )));
307 }
308 output_count
309 };
310
311 let mut outputs = TxOutputVec::new();
312
313 for _ in 0..output_count {
314 if data.len() < *offset + 8 {
315 return Err(ConsensusError::Serialization(Cow::Owned(
316 TransactionParseError::InsufficientBytes.to_string(),
317 )));
318 }
319 let value = i64::from_le_bytes([
320 data[*offset],
321 data[*offset + 1],
322 data[*offset + 2],
323 data[*offset + 3],
324 data[*offset + 4],
325 data[*offset + 5],
326 data[*offset + 6],
327 data[*offset + 7],
328 ]);
329 *offset += 8;
330
331 let (script_len, varint_len) = decode_varint(&data[*offset..])?;
332 *offset += varint_len;
333
334 let script_pubkey_end = checked_slice_end(*offset, script_len)?;
335 if data.len() < script_pubkey_end {
336 return Err(ConsensusError::Serialization(Cow::Owned(
337 TransactionParseError::InsufficientBytes.to_string(),
338 )));
339 }
340 let script_pubkey = data[*offset..script_pubkey_end].to_vec();
341 *offset = script_pubkey_end;
342
343 outputs.push(TransactionOutput {
344 value,
345 script_pubkey,
346 });
347 }
348
349 Ok(outputs)
350}
351
352pub fn deserialize_transaction_with_offset(data: &[u8]) -> Result<(Transaction, usize)> {
356 let (tx, _witnesses, bytes_consumed) = deserialize_transaction_with_witness(data)?;
357 Ok((tx, bytes_consumed))
358}
359
360pub fn deserialize_transaction_with_witness(
362 data: &[u8],
363) -> Result<(Transaction, Vec<Witness>, usize)> {
364 let mut offset = 0;
365
366 if data.len() < offset + 4 {
367 return Err(ConsensusError::Serialization(Cow::Owned(
368 TransactionParseError::InsufficientBytes.to_string(),
369 )));
370 }
371 let version = u32::from_le_bytes([
374 data[offset],
375 data[offset + 1],
376 data[offset + 2],
377 data[offset + 3],
378 ]) as u64;
379 offset += 4;
380
381 let (is_segwit, input_count, mut offset, implicit_empty_outputs) =
382 read_tx_input_count_after_version(data, offset)?;
383
384 let inputs = parse_inputs(data, &mut offset, input_count)?;
385 let outputs = parse_outputs(data, &mut offset, implicit_empty_outputs)?;
386
387 let mut all_witnesses: Vec<Witness> = Vec::new();
388 if is_segwit {
389 for _ in 0..input_count {
390 let (stack_count, varint_len) = decode_varint(&data[offset..])?;
391 offset += varint_len;
392
393 let mut witness_stack: Witness = Vec::new();
394 for _ in 0..stack_count {
395 let (item_len, varint_len) = decode_varint(&data[offset..])?;
396 offset += varint_len;
397
398 let item_end = checked_slice_end(offset, item_len)?;
399 if data.len() < item_end {
400 return Err(ConsensusError::Serialization(Cow::Owned(
401 TransactionParseError::InsufficientBytes.to_string(),
402 )));
403 }
404 witness_stack.push(data[offset..item_end].to_vec());
405 offset = item_end;
406 }
407 all_witnesses.push(witness_stack);
408 }
409 } else {
410 for _ in 0..input_count {
411 all_witnesses.push(Vec::new());
412 }
413 }
414
415 if data.len() < offset + 4 {
416 return Err(ConsensusError::Serialization(Cow::Owned(
417 TransactionParseError::InsufficientBytes.to_string(),
418 )));
419 }
420 let lock_time = u32::from_le_bytes([
421 data[offset],
422 data[offset + 1],
423 data[offset + 2],
424 data[offset + 3],
425 ]) as u64;
426 offset += 4;
427
428 let tx = Transaction {
429 version,
430 inputs,
431 outputs,
432 lock_time,
433 };
434
435 Ok((tx, all_witnesses, offset))
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441
442 #[test]
443 fn test_serialize_deserialize_round_trip() {
444 let tx = Transaction {
445 version: 1,
446 inputs: crate::tx_inputs![TransactionInput {
447 prevout: OutPoint {
448 hash: [1; 32],
449 index: 0
450 },
451 script_sig: vec![0x51],
452 sequence: 0xffffffff,
453 }],
454 outputs: crate::tx_outputs![TransactionOutput {
455 value: 5000000000,
456 script_pubkey: vec![0x51],
457 }],
458 lock_time: 0,
459 };
460
461 let serialized = serialize_transaction(&tx);
462 let deserialized = deserialize_transaction(&serialized).unwrap();
463
464 assert_eq!(deserialized.version, tx.version);
465 assert_eq!(deserialized.inputs.len(), tx.inputs.len());
466 assert_eq!(deserialized.outputs.len(), tx.outputs.len());
467 assert_eq!(deserialized.lock_time, tx.lock_time);
468 }
469
470 #[test]
472 fn empty_tx_round_trip_matches_double_zero_preamble() {
473 let tx = Transaction {
474 version: 1,
475 inputs: crate::tx_inputs![],
476 outputs: crate::tx_outputs![],
477 lock_time: 0,
478 };
479 let bytes = serialize_transaction(&tx);
480 let back = deserialize_transaction(&bytes).unwrap();
481 assert_eq!(back.version, tx.version);
482 assert!(back.inputs.is_empty());
483 assert!(back.outputs.is_empty());
484 assert_eq!(back.lock_time, tx.lock_time);
485 assert_eq!(&bytes[4..6], &[0u8, 0u8]);
487 }
488
489 #[test]
490 fn zero_inputs_one_output_round_trips_extended_framing() {
491 let tx = Transaction {
492 version: 2,
493 inputs: crate::tx_inputs![],
494 outputs: crate::tx_outputs![TransactionOutput {
495 value: 1000,
496 script_pubkey: vec![0x51],
497 }],
498 lock_time: 0x11223344,
499 };
500 let bytes = serialize_transaction(&tx);
501 let back = deserialize_transaction(&bytes).unwrap();
502 assert_eq!(back.version, tx.version);
503 assert!(back.inputs.is_empty());
504 assert_eq!(back.outputs.len(), 1);
505 assert_eq!(back.outputs[0].value, 1000);
506 assert_eq!(back.lock_time, tx.lock_time);
507 assert_eq!(&bytes[4..8], &[0u8, 1, 0, 1]);
509 }
510
511 #[test]
512 fn high_bit_version_stays_in_the_low_32_bits() {
513 let tx = Transaction {
514 version: 0xffff_ffff,
515 inputs: crate::tx_inputs![],
516 outputs: crate::tx_outputs![],
517 lock_time: 0,
518 };
519 let bytes = serialize_transaction(&tx);
520 assert_eq!(&bytes[0..4], &[0xff, 0xff, 0xff, 0xff]);
521 let back = deserialize_transaction(&bytes).unwrap();
522 assert_eq!(back.version, 0xffff_ffff);
523 assert!(back.version >= 2);
524
525 let witness_bytes = serialize_transaction_with_witness(&tx, &[]);
526 assert_eq!(&witness_bytes[0..4], &[0xff, 0xff, 0xff, 0xff]);
527 let (witness_back, stacks, _) =
528 deserialize_transaction_with_witness(&witness_bytes).unwrap();
529 assert_eq!(witness_back.version, 0xffff_ffff);
530 assert!(stacks.is_empty());
531 }
532}