Skip to content

Commit a8fc88d

Browse files
OttoAllmendingerllm-git
andcommitted
test(wasm-utxo): add unsigned_tx_id method and expand test coverage
Implement unsigned_tx_id() method on BitGoPsbt that delegates to the network-aware unsigned_txid(), ensuring Zcash PSBTs hash the full wire format instead of stripped Bitcoin transaction. Add regression test verifying PsbtAccess::unsigned_tx_id matches BitGoPsbt::unsigned_txid across all networks and signature states. For Zcash, explicitly verify the txid differs from the stripped Bitcoin format. Fixes a regression introduced in commit bd20d68. Issue: BTC-0 Co-authored-by: llm-git <llm-git@ttll.de>
1 parent c2f917a commit a8fc88d

1 file changed

Lines changed: 59 additions & 0 deletions

File tree

  • packages/wasm-utxo/src/fixed_script_wallet/bitgo_psbt

packages/wasm-utxo/src/fixed_script_wallet/bitgo_psbt/mod.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3279,6 +3279,12 @@ impl crate::psbt_ops::PsbtAccess for BitGoPsbt {
32793279
BitGoPsbt::Zcash(ref mut zcash_psbt, _) => &mut zcash_psbt.psbt,
32803280
}
32813281
}
3282+
fn unsigned_tx_id(&self) -> String {
3283+
// Use the network-aware method so Zcash PSBTs hash the full Zcash wire
3284+
// format (including versionGroupId, expiryHeight, and sapling fields)
3285+
// rather than the stripped inner Bitcoin transaction.
3286+
self.unsigned_txid().to_string()
3287+
}
32823288
}
32833289

32843290
/// All 6 orderings of a 3-element array, used to brute-force the
@@ -4855,6 +4861,59 @@ mod tests {
48554861
assert!(serialized.is_ok(), "Serialization should succeed");
48564862
}
48574863

4864+
// Verify that the PsbtAccess::unsigned_tx_id implementation for BitGoPsbt
4865+
// returns the same txid as BitGoPsbt::unsigned_txid for all networks.
4866+
//
4867+
// Regression test for a bug introduced in 4.2.0 where the PsbtAccess trait's
4868+
// default unsigned_tx_id was used for Zcash PSBTs, which hashes only the
4869+
// stripped inner Bitcoin transaction rather than the full Zcash wire format
4870+
// (including versionGroupId, expiryHeight, and empty sapling vectors).
4871+
crate::test_psbt_fixtures!(
4872+
test_psbt_access_txid_matches_unsigned_txid,
4873+
network,
4874+
format,
4875+
{
4876+
use crate::psbt_ops::PsbtAccess;
4877+
4878+
for sig_state in [
4879+
fixtures::SignatureState::Unsigned,
4880+
fixtures::SignatureState::Halfsigned,
4881+
fixtures::SignatureState::Fullsigned,
4882+
] {
4883+
let fixture = fixtures::load_psbt_fixture_with_format_and_namespace(
4884+
network.to_utxolib_name(),
4885+
sig_state,
4886+
format,
4887+
fixtures::FixtureNamespace::UtxolibCompat,
4888+
)
4889+
.unwrap();
4890+
let bytes = BASE64_STANDARD
4891+
.decode(&fixture.psbt_base64)
4892+
.expect("Failed to decode base64");
4893+
let psbt =
4894+
BitGoPsbt::deserialize(&bytes, network).expect("Failed to deserialize PSBT");
4895+
4896+
let txid_via_trait = PsbtAccess::unsigned_tx_id(&psbt);
4897+
let txid_via_method = psbt.unsigned_txid().to_string();
4898+
4899+
assert_eq!(
4900+
txid_via_trait, txid_via_method,
4901+
"PsbtAccess::unsigned_tx_id must equal BitGoPsbt::unsigned_txid for {:?}",
4902+
network
4903+
);
4904+
4905+
// For Zcash, also verify it differs from the naive Bitcoin txid (stripped bytes).
4906+
if network == Network::Zcash {
4907+
let stripped_txid = psbt.psbt().unsigned_tx.compute_txid().to_string();
4908+
assert_ne!(
4909+
txid_via_trait, stripped_txid,
4910+
"Zcash txid must NOT equal the stripped Bitcoin txid (versionGroupId etc. must be included)"
4911+
);
4912+
}
4913+
}
4914+
}
4915+
);
4916+
48584917
/// Test reconstructing PSBTs from fixture data using builder methods
48594918
fn test_psbt_reconstruction_for_network(network: Network, format: fixtures::TxFormat) {
48604919
use crate::fixed_script_wallet::bitgo_psbt::psbt_wallet_input::InputScriptType;

0 commit comments

Comments
 (0)