@@ -3696,7 +3696,7 @@ mod tests {
36963696 let prev_tx: Option < Vec < u8 > > = orig_psbt_input
36973697 . non_witness_utxo
36983698 . as_ref ( )
3699- . map ( |tx| miniscript:: bitcoin:: consensus:: serialize ( tx ) ) ;
3699+ . map ( miniscript:: bitcoin:: consensus:: serialize) ;
37003700
37013701 let result = reconstructed. add_wallet_input (
37023702 txid,
@@ -3730,7 +3730,7 @@ mod tests {
37303730 let prev_tx = orig_psbt_input
37313731 . non_witness_utxo
37323732 . as_ref ( )
3733- . map ( |tx| miniscript:: bitcoin:: consensus:: encode:: serialize ( tx ) ) ;
3733+ . map ( miniscript:: bitcoin:: consensus:: encode:: serialize) ;
37343734
37353735 reconstructed. add_replay_protection_input (
37363736 compressed_pubkey,
@@ -3853,9 +3853,6 @@ mod tests {
38533853 . enumerate ( )
38543854 {
38553855 // Compare utxo fields - either witness_utxo or non_witness_utxo should match
3856- // For segwit: witness_utxo is used
3857- // For non-segwit with prev_tx: non_witness_utxo is used
3858- // For non-segwit without prev_tx: witness_utxo is used as fallback
38593856 let orig_has_utxo = orig. witness_utxo . is_some ( ) || orig. non_witness_utxo . is_some ( ) ;
38603857 let recon_has_utxo = recon. witness_utxo . is_some ( ) || recon. non_witness_utxo . is_some ( ) ;
38613858 assert ! (
@@ -3906,7 +3903,6 @@ mod tests {
39063903 }
39073904
39083905 // For taproot wallet inputs, compare tap_internal_key
3909- // (but not tap_leaf_script which depends on signer/cosigner choice)
39103906 if orig. tap_internal_key . is_some ( ) {
39113907 assert_eq ! (
39123908 orig. tap_internal_key, recon. tap_internal_key,
@@ -3925,7 +3921,7 @@ mod tests {
39253921 . zip ( reconstructed_outputs. iter ( ) )
39263922 . enumerate ( )
39273923 {
3928- // Skip metadata comparison for non-wallet outputs (external or from different keys)
3924+ // Skip metadata comparison for non-wallet outputs
39293925 if !wallet_output_indices. contains ( & idx) {
39303926 continue ;
39313927 }
@@ -3971,4 +3967,97 @@ mod tests {
39713967 crate :: test_psbt_fixtures!( test_psbt_reconstruction, network, format, {
39723968 test_psbt_reconstruction_for_network( network, format) ;
39733969 } , ignore: [ Zcash ] ) ;
3970+
3971+ #[ test]
3972+ fn test_dogecoin_single_input_single_output_large_amount ( ) {
3973+ use crate :: fixed_script_wallet:: test_utils:: get_test_wallet_keys;
3974+ use miniscript:: bitcoin:: bip32:: { DerivationPath , Xpriv } ;
3975+ use miniscript:: bitcoin:: consensus:: { deserialize, serialize} ;
3976+ use miniscript:: bitcoin:: hashes:: { sha256, Hash } ;
3977+ use miniscript:: bitcoin:: psbt:: Psbt as BitcoinPsbt ;
3978+ use miniscript:: bitcoin:: secp256k1:: Secp256k1 ;
3979+ use miniscript:: bitcoin:: { Network as BitcoinNetwork , Txid } ;
3980+ use std:: str:: FromStr ;
3981+
3982+ let wallet_keys =
3983+ crate :: fixed_script_wallet:: RootWalletKeys :: new ( get_test_wallet_keys ( "doge_1e19" ) ) ;
3984+
3985+ let mut psbt = BitGoPsbt :: new ( Network :: Dogecoin , & wallet_keys, Some ( 2 ) , Some ( 0 ) ) ;
3986+
3987+ // Large output amount (1e19) should fit in u64 and round-trip.
3988+ let value: u64 = 10_000_000_000_000_000_000 ;
3989+
3990+ let txid = Txid :: all_zeros ( ) ;
3991+ let vout = 0u32 ;
3992+ let script_id = ScriptId { chain : 0 , index : 0 } ;
3993+
3994+ psbt. add_wallet_input (
3995+ txid,
3996+ vout,
3997+ value,
3998+ & wallet_keys,
3999+ script_id,
4000+ WalletInputOptions :: default ( ) ,
4001+ )
4002+ . expect ( "add_wallet_input" ) ;
4003+
4004+ psbt. add_wallet_output ( 0 , 0 , value, & wallet_keys)
4005+ . expect ( "add_wallet_output" ) ;
4006+
4007+ assert_eq ! ( psbt. psbt( ) . unsigned_tx. input. len( ) , 1 ) ;
4008+ assert_eq ! ( psbt. psbt( ) . unsigned_tx. output. len( ) , 1 ) ;
4009+ assert_eq ! ( psbt. psbt( ) . unsigned_tx. output[ 0 ] . value. to_sat( ) , value) ;
4010+
4011+ // Sign, finalize, and extract the signed transaction.
4012+ //
4013+ // This mirrors utxo-lib testutil.getKeyTriple("doge_1e19") which uses sha256("seed.{i}")
4014+ // and RootWalletKeys derives at m/0/0/{chain}/{index}.
4015+ let secp = Secp256k1 :: new ( ) ;
4016+ let seed = "doge_1e19" ;
4017+ let user_seed_hash = sha256:: Hash :: hash ( format ! ( "{}.0" , seed) . as_bytes ( ) ) . to_byte_array ( ) ;
4018+ let bitgo_seed_hash = sha256:: Hash :: hash ( format ! ( "{}.2" , seed) . as_bytes ( ) ) . to_byte_array ( ) ;
4019+ let user_xpriv =
4020+ Xpriv :: new_master ( BitcoinNetwork :: Testnet , & user_seed_hash) . expect ( "user xpriv" ) ;
4021+ let bitgo_xpriv =
4022+ Xpriv :: new_master ( BitcoinNetwork :: Testnet , & bitgo_seed_hash) . expect ( "bitgo xpriv" ) ;
4023+ let user_path = DerivationPath :: from_str ( "m/0/0/0/0" ) . expect ( "derivation path" ) ;
4024+ let derived = user_xpriv
4025+ . derive_priv ( & secp, & user_path)
4026+ . expect ( "derive user xpriv" ) ;
4027+ let user_privkey = derived. private_key ;
4028+ let derived_bitgo = bitgo_xpriv
4029+ . derive_priv ( & secp, & user_path)
4030+ . expect ( "derive bitgo xpriv" ) ;
4031+ let bitgo_privkey = derived_bitgo. private_key ;
4032+
4033+ psbt. sign_with_privkey ( 0 , & user_privkey)
4034+ . expect ( "sign_with_privkey" ) ;
4035+ psbt. sign_with_privkey ( 0 , & bitgo_privkey)
4036+ . expect ( "sign_with_privkey (bitgo)" ) ;
4037+
4038+ // P2SH multisig needs 2 signatures to finalize.
4039+ assert ! (
4040+ psbt. psbt( ) . inputs[ 0 ] . partial_sigs. len( ) >= 2 ,
4041+ "expected at least 2 partial signatures before finalization"
4042+ ) ;
4043+
4044+ let finalized_psbt: BitcoinPsbt = psbt. finalize ( & secp) . expect ( "finalize" ) ;
4045+ let extracted_tx = finalized_psbt. extract_tx ( ) . expect ( "extract_tx" ) ;
4046+ let extracted_bytes = serialize ( & extracted_tx) ;
4047+
4048+ // Sanity checks: has spend data and preserves amounts.
4049+ assert_eq ! ( extracted_tx. input. len( ) , 1 ) ;
4050+ assert ! (
4051+ !extracted_tx. input[ 0 ] . script_sig. is_empty( )
4052+ || !extracted_tx. input[ 0 ] . witness. is_empty( ) ,
4053+ "expected script_sig or witness to be present after finalization"
4054+ ) ;
4055+ assert_eq ! ( extracted_tx. output. len( ) , 1 ) ;
4056+ assert_eq ! ( extracted_tx. output[ 0 ] . value. to_sat( ) , value) ;
4057+
4058+ // Also ensure the extracted tx bytes can be decoded again.
4059+ let decoded = deserialize :: < miniscript:: bitcoin:: Transaction > ( & extracted_bytes)
4060+ . expect ( "decode extracted tx" ) ;
4061+ assert_eq ! ( decoded. compute_txid( ) , extracted_tx. compute_txid( ) ) ;
4062+ }
39744063}
0 commit comments