Skip to content

Commit 7cc4c8a

Browse files
committed
psbt: add unit test which covers round-tripping of pegins and various witnesses
This test case was written by Claude and (fairly heavily) edited by me to add the PSBT round-tripping and to tighten up some checks. Originally we thought the bug was related to witness encoding, so the test covers various permutations of null witnesses. Actually this was a red herring and the bug was just about the pegin flag in the input index being preserved across PSBTs. But I left most of the witness-deleting test in place anyway because it seems potentially useful.
1 parent d2224b5 commit 7cc4c8a

File tree

1 file changed

+67
-1
lines changed

1 file changed

+67
-1
lines changed

src/transaction.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1302,7 +1302,7 @@ impl ::std::error::Error for SighashTypeParseError {}
13021302
mod tests {
13031303
use std::str::FromStr;
13041304

1305-
use crate::encode::serialize;
1305+
use crate::{encode::serialize, pset::PartiallySignedTransaction};
13061306
use crate::confidential;
13071307
use crate::hex::FromHex;
13081308
use secp256k1_zkp::{self, ZERO_TWEAK};
@@ -2531,4 +2531,70 @@ mod tests {
25312531
assert!(tx3.input[0].asset_issuance.amount.explicit().unwrap() > max_money);
25322532
assert!(tx4.input[0].asset_issuance.inflation_keys.explicit().unwrap() > max_money);
25332533
}
2534+
2535+
#[test]
2536+
fn pset_pegin_witnesses_encoding_round_trip() {
2537+
use crate::encode::{serialize, deserialize};
2538+
2539+
// Start with a transaction that has a pegin.
2540+
let base_tx: Transaction = hex_deserialize!(include_str!("../tests/data/1in2out_pegin.hex"));
2541+
2542+
// Test case (a): input witnesses but no output witnesses
2543+
let mut tx_input_only = base_tx.clone();
2544+
for output in &mut tx_input_only.output {
2545+
output.witness = TxOutWitness::empty();
2546+
}
2547+
2548+
// Test case (b): output witnesses but no input witnesses
2549+
let mut tx_output_only = base_tx.clone();
2550+
for input in &mut tx_output_only.input {
2551+
input.witness = TxInWitness::empty();
2552+
}
2553+
2554+
// Test case (c): no witnesses at all
2555+
let mut tx_no_witnesses = base_tx.clone();
2556+
for input in &mut tx_no_witnesses.input {
2557+
input.witness = TxInWitness::empty();
2558+
}
2559+
for output in &mut tx_no_witnesses.output {
2560+
output.witness = TxOutWitness::empty();
2561+
}
2562+
2563+
// Test all cases: serialize then deserialize and verify they match
2564+
let test_cases = vec![
2565+
("input_witnesses_only", tx_input_only),
2566+
("output_witnesses_only", tx_output_only),
2567+
("no_witnesses", tx_no_witnesses),
2568+
("both_witnesses", base_tx),
2569+
];
2570+
2571+
for (name, original_tx) in test_cases {
2572+
let psbt = PartiallySignedTransaction::from_tx(original_tx.clone());
2573+
let psbt_tx = psbt.extract_tx().unwrap();
2574+
assert_eq!(original_tx, psbt_tx);
2575+
2576+
// Serialize the transaction
2577+
let serialized = serialize(&original_tx);
2578+
let serialized_psbt = serialize(&psbt_tx);
2579+
2580+
// Deserialize it back
2581+
let deserialized_tx: Transaction = deserialize(&serialized)
2582+
.unwrap_or_else(|e| panic!("Failed to deserialize {} transaction: {}", name, e));
2583+
let deserialized_psbt_tx: Transaction = deserialize(&serialized_psbt)
2584+
.unwrap_or_else(|e| panic!("Failed to deserialize {} transaction: {}", name, e));
2585+
2586+
// Verify they match exactly
2587+
assert_eq!(original_tx, deserialized_tx,
2588+
"Roundtrip failed for {} transaction", name);
2589+
assert_eq!(original_tx, deserialized_psbt_tx,
2590+
"Roundtrip failed for {} transaction", name);
2591+
2592+
// Verify reserialization is consistent
2593+
let reserialized = serialize(&deserialized_tx);
2594+
assert_eq!(serialized.len(), reserialized.len(),
2595+
"Serialized length changed for {} transaction", name);
2596+
assert_eq!(serialized, reserialized,
2597+
"Serialized bytes changed for {} transaction", name);
2598+
}
2599+
}
25342600
}

0 commit comments

Comments
 (0)