@@ -633,6 +633,98 @@ async fn tx_can_broadcast() {
633633 . unwrap ( ) ;
634634}
635635
636+ // Verify that broadcasting a transaction that the peer already has.
637+ #[ tokio:: test]
638+ async fn tx_broadcast_does_not_hang_when_peer_has_tx ( ) {
639+ let amount_to_us = Amount :: from_sat ( 100_000 ) ;
640+ let amount_to_op_return = Amount :: from_sat ( 50_000 ) ;
641+ let ( bitcoind, socket_addr) = start_bitcoind ( true ) . unwrap ( ) ;
642+ let rpc = & bitcoind. client ;
643+ let tempdir = tempfile:: TempDir :: new ( ) . unwrap ( ) . path ( ) . to_owned ( ) ;
644+ let mut rng = StdRng :: seed_from_u64 ( 20002 ) ;
645+ let secret = SecretKey :: new ( & mut rng) ;
646+ let secp = Secp256k1 :: new ( ) ;
647+ let keypair = Keypair :: from_secret_key ( & secp, & secret) ;
648+ let ( internal_key, _) = keypair. x_only_public_key ( ) ;
649+ let send_to_this_address = Address :: p2tr ( & secp, internal_key, None , KnownHrp :: Regtest ) ;
650+ let miner = rpc. new_address ( ) . unwrap ( ) ;
651+ mine_blocks ( rpc, & miner, 110 , 10 ) . await ;
652+ let tx_info = rpc
653+ . send_to_address ( & send_to_this_address, amount_to_us)
654+ . unwrap ( ) ;
655+ let txid = tx_info. txid ( ) . unwrap ( ) ;
656+ let tx_details = rpc. get_transaction ( txid) . unwrap ( ) . details ;
657+ let ( vout, amt) = tx_details
658+ . iter ( )
659+ . find ( |detail| detail. address . eq ( & send_to_this_address. to_string ( ) ) )
660+ . map ( |detail| ( detail. vout , detail. amount ) )
661+ . unwrap ( ) ;
662+ let txout = TxOut {
663+ script_pubkey : miner. script_pubkey ( ) ,
664+ value : amount_to_op_return,
665+ } ;
666+ let outpoint = OutPoint { txid, vout } ;
667+ let txin = TxIn {
668+ previous_output : outpoint,
669+ script_sig : ScriptBuf :: default ( ) ,
670+ sequence : Sequence :: ENABLE_RBF_NO_LOCKTIME ,
671+ witness : Witness :: default ( ) ,
672+ } ;
673+ let mut unsigned_tx = Transaction {
674+ version : bitcoin:: transaction:: Version :: TWO ,
675+ lock_time : absolute:: LockTime :: ZERO ,
676+ input : vec ! [ txin] ,
677+ output : vec ! [ txout] ,
678+ } ;
679+ let input_index = 0 ;
680+ let sighash_type = TapSighashType :: Default ;
681+ let prevout = TxOut {
682+ script_pubkey : send_to_this_address. script_pubkey ( ) ,
683+ value : Amount :: from_btc ( amt. abs ( ) ) . unwrap ( ) ,
684+ } ;
685+ let prevouts = vec ! [ prevout] ;
686+ let prevouts = Prevouts :: All ( & prevouts) ;
687+ let mut sighasher = SighashCache :: new ( & mut unsigned_tx) ;
688+ let sighash = sighasher
689+ . taproot_key_spend_signature_hash ( input_index, & prevouts, sighash_type)
690+ . unwrap ( ) ;
691+ let tweaked: bitcoin:: key:: TweakedKeypair = keypair. tap_tweak ( & secp, None ) ;
692+ let msg = bitcoin:: secp256k1:: Message :: from ( sighash) ;
693+ let signature = secp. sign_schnorr ( & msg, & tweaked. to_keypair ( ) ) ;
694+ let signature = bitcoin:: taproot:: Signature {
695+ signature,
696+ sighash_type,
697+ } ;
698+ * sighasher. witness_mut ( input_index) . unwrap ( ) = Witness :: p2tr_key_spend ( & signature) ;
699+ let tx = sighasher. into_transaction ( ) . to_owned ( ) ;
700+ // Submit the tx directly to bitcoind via RPC — now the peer already has it.
701+ rpc. send_raw_transaction ( & tx) . unwrap ( ) ;
702+ println ! ( "Submitted tx {} to bitcoind via RPC" , tx. compute_txid( ) ) ;
703+ // Now broadcast the same tx via kyoto with a zero timeout.
704+ let host = ( IpAddr :: V4 ( * socket_addr. ip ( ) ) , Some ( socket_addr. port ( ) ) ) ;
705+ let ( node, client) = bip157:: Builder :: new ( bitcoin:: Network :: Regtest )
706+ . add_peer ( host)
707+ . data_dir ( tempdir)
708+ . chain_state ( ChainState :: Checkpoint ( HeaderCheckpoint :: from_genesis (
709+ bitcoin:: Network :: Regtest ,
710+ ) ) )
711+ . broadcast_timeout ( Duration :: ZERO )
712+ . build ( ) ;
713+ tokio:: task:: spawn ( async move { node. run ( ) . await } ) ;
714+ let Client {
715+ requester,
716+ info_rx : _,
717+ warn_rx : _,
718+ event_rx : _,
719+ } = client;
720+ let result = requester. broadcast_tx ( tx) . await ;
721+ assert ! (
722+ matches!( result, Err ( bip157:: ClientError :: BroadcastTimeout ) ) ,
723+ "expected BroadcastTimeout, got: {:?}" ,
724+ result,
725+ ) ;
726+ }
727+
636728#[ tokio:: test]
637729async fn dns_works ( ) {
638730 let hostname = bip157:: lookup_host ( "seed.bitcoin.sipa.be" ) . await ;
0 commit comments