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 SuperfluousWitness,
25}
26
27impl std::fmt::Display for TransactionParseError {
28 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
29 match self {
30 TransactionParseError::InsufficientBytes => {
31 write!(f, "Insufficient bytes to parse transaction")
32 }
33 TransactionParseError::InvalidVersion => write!(f, "Invalid transaction version"),
34 TransactionParseError::InvalidInputCount => write!(f, "Invalid input count"),
35 TransactionParseError::InvalidOutputCount => write!(f, "Invalid output count"),
36 TransactionParseError::InvalidScriptLength => write!(f, "Invalid script length"),
37 TransactionParseError::InvalidLockTime => write!(f, "Invalid lock time"),
38 TransactionParseError::SuperfluousWitness => {
39 write!(f, "Witness flag set but every witness stack is empty")
40 }
41 }
42 }
43}
44
45impl std::error::Error for TransactionParseError {}
46
47#[inline]
48fn checked_slice_end(offset: usize, len: u64) -> Result<usize> {
49 let len = usize::try_from(len).map_err(|_| {
50 ConsensusError::Serialization(Cow::Owned(
51 TransactionParseError::InvalidScriptLength.to_string(),
52 ))
53 })?;
54 offset.checked_add(len).ok_or_else(|| {
55 ConsensusError::Serialization(Cow::Owned(
56 TransactionParseError::InsufficientBytes.to_string(),
57 ))
58 })
59}
60
61fn read_tx_input_count_after_version(
72 data: &[u8],
73 mut offset: usize,
74) -> Result<(bool, u64, usize, bool)> {
75 let (mut input_count, varint_len) = decode_varint(&data[offset..])?;
76 offset += varint_len;
77
78 if input_count > 1_000_000 {
79 return Err(ConsensusError::Serialization(Cow::Owned(
80 TransactionParseError::InvalidInputCount.to_string(),
81 )));
82 }
83
84 let mut is_segwit = false;
85 let mut implicit_empty_outputs = false;
87
88 if input_count == 0 {
89 if offset >= data.len() {
90 return Err(ConsensusError::Serialization(Cow::Owned(
91 TransactionParseError::InsufficientBytes.to_string(),
92 )));
93 }
94 let flag = data[offset];
95 offset += 1;
96
97 if flag == 0 {
98 implicit_empty_outputs = true;
99 return Ok((false, 0, offset, implicit_empty_outputs));
100 }
101
102 if flag != 1 {
103 return Err(ConsensusError::Serialization(Cow::Owned(format!(
104 "Unsupported segwit transaction flag: {flag}"
105 ))));
106 }
107
108 is_segwit = true;
109
110 let (ic2, vl2) = decode_varint(&data[offset..])?;
111 offset += vl2;
112 if ic2 > 1_000_000 {
113 return Err(ConsensusError::Serialization(Cow::Owned(
114 TransactionParseError::InvalidInputCount.to_string(),
115 )));
116 }
117 input_count = ic2;
118 }
119
120 Ok((is_segwit, input_count, offset, implicit_empty_outputs))
121}
122
123#[inline(always)]
125pub fn serialize_transaction(tx: &Transaction) -> Vec<u8> {
126 let mut result = Vec::new();
127 serialize_transaction_append(&mut result, tx);
128 result
129}
130
131#[inline(always)]
139fn serialize_transaction_append(result: &mut Vec<u8>, tx: &Transaction) {
140 result.extend_from_slice(&(tx.version as u32).to_le_bytes());
141
142 if tx.inputs.is_empty() && !tx.outputs.is_empty() {
143 result.extend_from_slice(&encode_varint(0));
144 result.push(0x01);
145 result.extend_from_slice(&encode_varint(tx.inputs.len() as u64));
146 } else {
147 result.extend_from_slice(&encode_varint(tx.inputs.len() as u64));
148 }
149
150 for input in &tx.inputs {
151 result.extend_from_slice(&input.prevout.hash);
152 result.extend_from_slice(&input.prevout.index.to_le_bytes());
153 result.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
154 result.extend_from_slice(&input.script_sig);
155 result.extend_from_slice(&(input.sequence as u32).to_le_bytes());
156 }
157
158 result.extend_from_slice(&encode_varint(tx.outputs.len() as u64));
159
160 for output in &tx.outputs {
161 result.extend_from_slice(&(output.value as u64).to_le_bytes());
162 result.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
163 result.extend_from_slice(&output.script_pubkey);
164 }
165
166 result.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
167}
168
169#[inline(always)]
171pub fn serialize_transaction_into(dst: &mut Vec<u8>, tx: &Transaction) -> usize {
172 dst.clear();
173 serialize_transaction_append(dst, tx);
174 dst.len()
175}
176
177pub fn serialize_transaction_with_witness(
181 tx: &Transaction,
182 witnesses: &[Witness],
183) -> Result<Vec<u8>> {
184 if witnesses.len() != tx.inputs.len() {
185 return Err(ConsensusError::TransactionValidation(
186 format!(
187 "witness count {} must match input count {}",
188 witnesses.len(),
189 tx.inputs.len()
190 )
191 .into(),
192 ));
193 }
194 if !tx.inputs.is_empty() && witnesses.iter().all(|stack| stack.is_empty()) {
198 return Ok(serialize_transaction(tx));
199 }
200 let mut result = Vec::new();
201 result.extend_from_slice(&(tx.version as u32).to_le_bytes());
202 result.push(0x00);
203 result.push(0x01);
204 result.extend_from_slice(&encode_varint(tx.inputs.len() as u64));
205 for input in &tx.inputs {
206 result.extend_from_slice(&input.prevout.hash);
207 result.extend_from_slice(&input.prevout.index.to_le_bytes());
208 result.extend_from_slice(&encode_varint(input.script_sig.len() as u64));
209 result.extend_from_slice(&input.script_sig);
210 result.extend_from_slice(&(input.sequence as u32).to_le_bytes());
211 }
212 result.extend_from_slice(&encode_varint(tx.outputs.len() as u64));
213 for output in &tx.outputs {
214 result.extend_from_slice(&(output.value as u64).to_le_bytes());
215 result.extend_from_slice(&encode_varint(output.script_pubkey.len() as u64));
216 result.extend_from_slice(&output.script_pubkey);
217 }
218 for witness in witnesses {
219 result.extend_from_slice(&encode_varint(witness.len() as u64));
220 for element in witness {
221 result.extend_from_slice(&encode_varint(element.len() as u64));
222 result.extend_from_slice(element);
223 }
224 }
225 result.extend_from_slice(&(tx.lock_time as u32).to_le_bytes());
226 Ok(result)
227}
228
229pub fn deserialize_transaction(data: &[u8]) -> Result<Transaction> {
234 deserialize_transaction_with_witness(data).map(|(tx, _, _)| tx)
235}
236
237#[cfg(feature = "production")]
243type TxInputVec = SmallVec<[TransactionInput; 2]>;
244#[cfg(not(feature = "production"))]
245type TxInputVec = Vec<TransactionInput>;
246
247#[cfg(feature = "production")]
248type TxOutputVec = SmallVec<[TransactionOutput; 2]>;
249#[cfg(not(feature = "production"))]
250type TxOutputVec = Vec<TransactionOutput>;
251
252#[inline]
254fn parse_inputs(data: &[u8], offset: &mut usize, count: u64) -> Result<TxInputVec> {
255 let mut inputs = TxInputVec::new();
256
257 for _ in 0..count {
258 if data.len() < *offset + 36 {
259 return Err(ConsensusError::Serialization(Cow::Owned(
260 TransactionParseError::InsufficientBytes.to_string(),
261 )));
262 }
263 let mut hash = [0u8; 32];
264 hash.copy_from_slice(&data[*offset..*offset + 32]);
265 *offset += 32;
266
267 let index = u32::from_le_bytes([
268 data[*offset],
269 data[*offset + 1],
270 data[*offset + 2],
271 data[*offset + 3],
272 ]);
273 *offset += 4;
274
275 let (script_len, varint_len) = decode_varint(&data[*offset..])?;
276 *offset += varint_len;
277
278 let script_sig_end = checked_slice_end(*offset, script_len)?;
279 if data.len() < script_sig_end {
280 return Err(ConsensusError::Serialization(Cow::Owned(
281 TransactionParseError::InsufficientBytes.to_string(),
282 )));
283 }
284 let script_sig = data[*offset..script_sig_end].to_vec();
285 *offset = script_sig_end;
286
287 if data.len() < *offset + 4 {
288 return Err(ConsensusError::Serialization(Cow::Owned(
289 TransactionParseError::InsufficientBytes.to_string(),
290 )));
291 }
292 let sequence = u32::from_le_bytes([
293 data[*offset],
294 data[*offset + 1],
295 data[*offset + 2],
296 data[*offset + 3],
297 ]) as u64;
298 *offset += 4;
299
300 inputs.push(TransactionInput {
301 prevout: OutPoint { hash, index },
302 script_sig,
303 sequence,
304 });
305 }
306
307 Ok(inputs)
308}
309
310#[inline]
313fn parse_outputs(
314 data: &[u8],
315 offset: &mut usize,
316 implicit_empty_outputs: bool,
317) -> Result<TxOutputVec> {
318 let output_count = if implicit_empty_outputs {
319 0
320 } else {
321 let (output_count, varint_len) = decode_varint(&data[*offset..])?;
322 *offset += varint_len;
323
324 if output_count > 1_000_000 {
325 return Err(ConsensusError::Serialization(Cow::Owned(
326 TransactionParseError::InvalidOutputCount.to_string(),
327 )));
328 }
329 output_count
330 };
331
332 let mut outputs = TxOutputVec::new();
333
334 for _ in 0..output_count {
335 if data.len() < *offset + 8 {
336 return Err(ConsensusError::Serialization(Cow::Owned(
337 TransactionParseError::InsufficientBytes.to_string(),
338 )));
339 }
340 let value = i64::from_le_bytes([
341 data[*offset],
342 data[*offset + 1],
343 data[*offset + 2],
344 data[*offset + 3],
345 data[*offset + 4],
346 data[*offset + 5],
347 data[*offset + 6],
348 data[*offset + 7],
349 ]);
350 *offset += 8;
351
352 let (script_len, varint_len) = decode_varint(&data[*offset..])?;
353 *offset += varint_len;
354
355 let script_pubkey_end = checked_slice_end(*offset, script_len)?;
356 if data.len() < script_pubkey_end {
357 return Err(ConsensusError::Serialization(Cow::Owned(
358 TransactionParseError::InsufficientBytes.to_string(),
359 )));
360 }
361 let script_pubkey = data[*offset..script_pubkey_end].to_vec();
362 *offset = script_pubkey_end;
363
364 outputs.push(TransactionOutput {
365 value,
366 script_pubkey,
367 });
368 }
369
370 Ok(outputs)
371}
372
373pub fn deserialize_transaction_with_offset(data: &[u8]) -> Result<(Transaction, usize)> {
377 let (tx, _witnesses, bytes_consumed) = deserialize_transaction_with_witness(data)?;
378 Ok((tx, bytes_consumed))
379}
380
381pub fn deserialize_transaction_with_witness(
383 data: &[u8],
384) -> Result<(Transaction, Vec<Witness>, usize)> {
385 let mut offset = 0;
386
387 if data.len() < offset + 4 {
388 return Err(ConsensusError::Serialization(Cow::Owned(
389 TransactionParseError::InsufficientBytes.to_string(),
390 )));
391 }
392 let version = u32::from_le_bytes([
395 data[offset],
396 data[offset + 1],
397 data[offset + 2],
398 data[offset + 3],
399 ]) as u64;
400 offset += 4;
401
402 let (is_segwit, input_count, mut offset, implicit_empty_outputs) =
403 read_tx_input_count_after_version(data, offset)?;
404
405 let inputs = parse_inputs(data, &mut offset, input_count)?;
406 let outputs = parse_outputs(data, &mut offset, implicit_empty_outputs)?;
407
408 let mut all_witnesses: Vec<Witness> = Vec::new();
409 if is_segwit {
410 for _ in 0..input_count {
411 let (stack_count, varint_len) = decode_varint(&data[offset..])?;
412 offset += varint_len;
413
414 let mut witness_stack: Witness = Vec::new();
415 for _ in 0..stack_count {
416 let (item_len, varint_len) = decode_varint(&data[offset..])?;
417 offset += varint_len;
418
419 let item_end = checked_slice_end(offset, item_len)?;
420 if data.len() < item_end {
421 return Err(ConsensusError::Serialization(Cow::Owned(
422 TransactionParseError::InsufficientBytes.to_string(),
423 )));
424 }
425 witness_stack.push(data[offset..item_end].to_vec());
426 offset = item_end;
427 }
428 all_witnesses.push(witness_stack);
429 }
430 if input_count > 0 && all_witnesses.iter().all(|stack| stack.is_empty()) {
434 return Err(ConsensusError::Serialization(Cow::Owned(
435 TransactionParseError::SuperfluousWitness.to_string(),
436 )));
437 }
438 } else {
439 for _ in 0..input_count {
440 all_witnesses.push(Vec::new());
441 }
442 }
443
444 if data.len() < offset + 4 {
445 return Err(ConsensusError::Serialization(Cow::Owned(
446 TransactionParseError::InsufficientBytes.to_string(),
447 )));
448 }
449 let lock_time = u32::from_le_bytes([
450 data[offset],
451 data[offset + 1],
452 data[offset + 2],
453 data[offset + 3],
454 ]) as u64;
455 offset += 4;
456
457 let tx = Transaction {
458 version,
459 inputs,
460 outputs,
461 lock_time,
462 };
463
464 Ok((tx, all_witnesses, offset))
465}
466
467pub fn output_script_pubkey_slices_from_wire_tx(data: &[u8]) -> Result<(Vec<&[u8]>, usize)> {
471 if data.len() < 4 {
472 return Err(ConsensusError::Serialization(Cow::Owned(
473 TransactionParseError::InsufficientBytes.to_string(),
474 )));
475 }
476 let (is_segwit, input_count, mut offset, implicit_empty_outputs) =
477 read_tx_input_count_after_version(data, 4)?;
478 skip_input_bodies(data, &mut offset, input_count)?;
479 let scripts = take_output_scripts(data, &mut offset, implicit_empty_outputs)?;
480 if is_segwit {
481 skip_witness_bodies(data, &mut offset, input_count)?;
482 }
483 if data.len() < offset + 4 {
484 return Err(ConsensusError::Serialization(Cow::Owned(
485 TransactionParseError::InsufficientBytes.to_string(),
486 )));
487 }
488 offset += 4;
489 Ok((scripts, offset))
490}
491
492fn skip_input_bodies(data: &[u8], offset: &mut usize, count: u64) -> Result<()> {
493 for _ in 0..count {
494 if data.len() < *offset + 36 {
495 return Err(ConsensusError::Serialization(Cow::Owned(
496 TransactionParseError::InsufficientBytes.to_string(),
497 )));
498 }
499 *offset += 36;
500 let (script_len, varint_len) = decode_varint(&data[*offset..])?;
501 *offset += varint_len;
502 let script_sig_end = checked_slice_end(*offset, script_len)?;
503 if data.len() < script_sig_end {
504 return Err(ConsensusError::Serialization(Cow::Owned(
505 TransactionParseError::InsufficientBytes.to_string(),
506 )));
507 }
508 *offset = script_sig_end;
509 if data.len() < *offset + 4 {
510 return Err(ConsensusError::Serialization(Cow::Owned(
511 TransactionParseError::InsufficientBytes.to_string(),
512 )));
513 }
514 *offset += 4;
515 }
516 Ok(())
517}
518
519fn take_output_scripts<'a>(
520 data: &'a [u8],
521 offset: &mut usize,
522 implicit_empty_outputs: bool,
523) -> Result<Vec<&'a [u8]>> {
524 let output_count = if implicit_empty_outputs {
525 0
526 } else {
527 let (output_count, varint_len) = decode_varint(&data[*offset..])?;
528 *offset += varint_len;
529 if output_count > 1_000_000 {
530 return Err(ConsensusError::Serialization(Cow::Owned(
531 TransactionParseError::InvalidOutputCount.to_string(),
532 )));
533 }
534 output_count
535 };
536 let mut scripts = Vec::with_capacity(usize::try_from(output_count).unwrap_or(0));
537 for _ in 0..output_count {
538 if data.len() < *offset + 8 {
539 return Err(ConsensusError::Serialization(Cow::Owned(
540 TransactionParseError::InsufficientBytes.to_string(),
541 )));
542 }
543 *offset += 8;
544 let (script_len, varint_len) = decode_varint(&data[*offset..])?;
545 *offset += varint_len;
546 let script_end = checked_slice_end(*offset, script_len)?;
547 if data.len() < script_end {
548 return Err(ConsensusError::Serialization(Cow::Owned(
549 TransactionParseError::InsufficientBytes.to_string(),
550 )));
551 }
552 scripts.push(&data[*offset..script_end]);
553 *offset = script_end;
554 }
555 Ok(scripts)
556}
557
558fn skip_witness_bodies(data: &[u8], offset: &mut usize, input_count: u64) -> Result<()> {
559 let mut any_stack = false;
560 for _ in 0..input_count {
561 let (stack_count, varint_len) = decode_varint(&data[*offset..])?;
562 *offset += varint_len;
563 if stack_count > 0 {
564 any_stack = true;
565 }
566 for _ in 0..stack_count {
567 let (item_len, varint_len) = decode_varint(&data[*offset..])?;
568 *offset += varint_len;
569 let item_end = checked_slice_end(*offset, item_len)?;
570 if data.len() < item_end {
571 return Err(ConsensusError::Serialization(Cow::Owned(
572 TransactionParseError::InsufficientBytes.to_string(),
573 )));
574 }
575 *offset = item_end;
576 }
577 }
578 if input_count > 0 && !any_stack {
579 return Err(ConsensusError::Serialization(Cow::Owned(
580 TransactionParseError::SuperfluousWitness.to_string(),
581 )));
582 }
583 Ok(())
584}
585
586#[cfg(test)]
587mod tests {
588 use super::*;
589
590 #[test]
591 fn test_serialize_deserialize_round_trip() {
592 let tx = Transaction {
593 version: 1,
594 inputs: crate::tx_inputs![TransactionInput {
595 prevout: OutPoint {
596 hash: [1; 32],
597 index: 0
598 },
599 script_sig: vec![0x51],
600 sequence: 0xffffffff,
601 }],
602 outputs: crate::tx_outputs![TransactionOutput {
603 value: 5000000000,
604 script_pubkey: vec![0x51],
605 }],
606 lock_time: 0,
607 };
608
609 let serialized = serialize_transaction(&tx);
610 let deserialized = deserialize_transaction(&serialized).unwrap();
611
612 assert_eq!(deserialized.version, tx.version);
613 assert_eq!(deserialized.inputs.len(), tx.inputs.len());
614 assert_eq!(deserialized.outputs.len(), tx.outputs.len());
615 assert_eq!(deserialized.lock_time, tx.lock_time);
616 }
617
618 #[test]
620 fn empty_tx_round_trip_matches_double_zero_preamble() {
621 let tx = Transaction {
622 version: 1,
623 inputs: crate::tx_inputs![],
624 outputs: crate::tx_outputs![],
625 lock_time: 0,
626 };
627 let bytes = serialize_transaction(&tx);
628 let back = deserialize_transaction(&bytes).unwrap();
629 assert_eq!(back.version, tx.version);
630 assert!(back.inputs.is_empty());
631 assert!(back.outputs.is_empty());
632 assert_eq!(back.lock_time, tx.lock_time);
633 assert_eq!(&bytes[4..6], &[0u8, 0u8]);
635 }
636
637 #[test]
638 fn zero_inputs_one_output_round_trips_extended_framing() {
639 let tx = Transaction {
640 version: 2,
641 inputs: crate::tx_inputs![],
642 outputs: crate::tx_outputs![TransactionOutput {
643 value: 1000,
644 script_pubkey: vec![0x51],
645 }],
646 lock_time: 0x11223344,
647 };
648 let bytes = serialize_transaction(&tx);
649 let back = deserialize_transaction(&bytes).unwrap();
650 assert_eq!(back.version, tx.version);
651 assert!(back.inputs.is_empty());
652 assert_eq!(back.outputs.len(), 1);
653 assert_eq!(back.outputs[0].value, 1000);
654 assert_eq!(back.lock_time, tx.lock_time);
655 assert_eq!(&bytes[4..8], &[0u8, 1, 0, 1]);
657 }
658
659 #[test]
660 fn high_bit_version_stays_in_the_low_32_bits() {
661 let tx = Transaction {
662 version: 0xffff_ffff,
663 inputs: crate::tx_inputs![],
664 outputs: crate::tx_outputs![],
665 lock_time: 0,
666 };
667 let bytes = serialize_transaction(&tx);
668 assert_eq!(&bytes[0..4], &[0xff, 0xff, 0xff, 0xff]);
669 let back = deserialize_transaction(&bytes).unwrap();
670 assert_eq!(back.version, 0xffff_ffff);
671 assert!(back.version >= 2);
672
673 let witness_bytes = serialize_transaction_with_witness(&tx, &[]).unwrap();
674 assert_eq!(&witness_bytes[0..4], &[0xff, 0xff, 0xff, 0xff]);
675 let (witness_back, stacks, _) =
676 deserialize_transaction_with_witness(&witness_bytes).unwrap();
677 assert_eq!(witness_back.version, 0xffff_ffff);
678 assert!(stacks.is_empty());
679 }
680
681 #[test]
682 fn witness_flag_with_only_empty_stacks_does_not_decode() {
683 let bytes = hex_bytes(
685 "0100000000010100000000000000000000000000000000000000000000000000\
686 00000000000000ffffffff00ffffffff010000000000000000000000000000",
687 );
688 let err = deserialize_transaction_with_witness(&bytes).unwrap_err();
689 let msg = err.to_string();
690 assert!(
691 msg.contains("every witness stack is empty"),
692 "unexpected error: {msg}"
693 );
694 }
695
696 #[test]
697 fn witness_stack_of_one_empty_item_still_decodes() {
698 let tx = Transaction {
699 version: 1,
700 inputs: crate::tx_inputs![TransactionInput {
701 prevout: OutPoint {
702 hash: [0; 32],
703 index: 0xffffffff,
704 },
705 script_sig: vec![],
706 sequence: 0xffffffff,
707 }],
708 outputs: crate::tx_outputs![TransactionOutput {
709 value: 0,
710 script_pubkey: vec![],
711 }],
712 lock_time: 0,
713 };
714 let bytes = serialize_transaction_with_witness(&tx, &[vec![vec![]]]).unwrap();
715 let (back, stacks, consumed) = deserialize_transaction_with_witness(&bytes).unwrap();
716 assert_eq!(consumed, bytes.len());
717 assert_eq!(back.inputs.len(), 1);
718 assert_eq!(stacks.len(), 1);
719 assert_eq!(stacks[0].len(), 1);
720 assert!(stacks[0][0].is_empty());
721 }
722
723 #[test]
724 fn empty_stacks_serialize_without_the_witness_flag() {
725 let tx = Transaction {
726 version: 1,
727 inputs: crate::tx_inputs![TransactionInput {
728 prevout: OutPoint {
729 hash: [2; 32],
730 index: 1,
731 },
732 script_sig: vec![0x51],
733 sequence: 0xfffffffe,
734 }],
735 outputs: crate::tx_outputs![TransactionOutput {
736 value: 50,
737 script_pubkey: vec![0x51],
738 }],
739 lock_time: 0,
740 };
741 let with_empty = serialize_transaction_with_witness(&tx, &[Vec::new()]).unwrap();
742 let legacy = serialize_transaction(&tx);
743 assert_eq!(with_empty, legacy);
744 let (back, stacks, _) = deserialize_transaction_with_witness(&with_empty).unwrap();
745 assert_eq!(back.inputs.len(), 1);
746 assert!(stacks[0].is_empty());
747 }
748
749 #[test]
750 fn witness_count_mismatch_is_an_error() {
751 let tx = Transaction {
752 version: 1,
753 inputs: crate::tx_inputs![TransactionInput {
754 prevout: OutPoint {
755 hash: [3; 32],
756 index: 0,
757 },
758 script_sig: vec![],
759 sequence: 0xffffffff,
760 }],
761 outputs: crate::tx_outputs![TransactionOutput {
762 value: 1,
763 script_pubkey: vec![],
764 }],
765 lock_time: 0,
766 };
767 assert!(serialize_transaction_with_witness(&tx, &[]).is_err());
768 assert!(serialize_transaction_with_witness(&tx, &[vec![vec![1]], vec![vec![2]]]).is_err());
769 assert!(serialize_transaction_with_witness(&tx, &[vec![vec![1]]]).is_ok());
770 }
771
772 fn hex_bytes(s: &str) -> Vec<u8> {
773 (0..s.len())
774 .step_by(2)
775 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
776 .collect()
777 }
778}