@@ -898,75 +898,126 @@ impl<S: MutinyStorage> NodeManager<S> {
898898 Ok ( enroller. process_res ( ohttp_response. as_ref ( ) , context) ?)
899899 }
900900
901- // Send v1 payjoin request
901+ // Send v2 payjoin request
902902 pub async fn send_payjoin (
903903 & self ,
904904 uri : Uri < ' _ , payjoin:: bitcoin:: address:: NetworkChecked > ,
905905 amount : u64 ,
906906 labels : Vec < String > ,
907907 fee_rate : Option < f32 > ,
908- ) -> Result < Txid , MutinyError > {
908+ ) -> Result < ( ) , MutinyError > {
909909 let address = Address :: from_str ( & uri. address . to_string ( ) )
910910 . map_err ( |_| MutinyError :: PayjoinConfigError ) ?;
911911 let original_psbt = self . wallet . create_signed_psbt ( address, amount, fee_rate) ?;
912-
912+ // TODO ensure this creates a pending tx in the UI. Ensure locked UTXO.
913913 let fee_rate = if let Some ( rate) = fee_rate {
914914 FeeRate :: from_sat_per_vb ( rate)
915915 } else {
916916 let sat_per_kwu = self . fee_estimator . get_normal_fee_rate ( ) ;
917917 FeeRate :: from_sat_per_kwu ( sat_per_kwu as f32 )
918918 } ;
919919 let fee_rate = payjoin:: bitcoin:: FeeRate :: from_sat_per_kwu ( fee_rate. sat_per_kwu ( ) as u64 ) ;
920- let original_psbt = payjoin:: bitcoin:: psbt:: PartiallySignedTransaction :: from_str (
920+ let original_psbt_30 = payjoin:: bitcoin:: psbt:: PartiallySignedTransaction :: from_str (
921921 & original_psbt. to_string ( ) ,
922922 )
923923 . map_err ( |_| MutinyError :: PayjoinConfigError ) ?;
924924 log_debug ! ( self . logger, "Creating payjoin request" ) ;
925- let ( req, ctx) =
926- payjoin:: send:: RequestBuilder :: from_psbt_and_uri ( original_psbt. clone ( ) , uri)
927- . unwrap ( )
928- . build_recommended ( fee_rate)
929- . map_err ( |_| MutinyError :: PayjoinConfigError ) ?
930- . extract_v1 ( ) ?;
931-
932- let client = Client :: builder ( )
933- . build ( )
925+ let req_ctx = payjoin:: send:: RequestBuilder :: from_psbt_and_uri ( original_psbt_30, uri)
926+ . unwrap ( )
927+ . build_recommended ( fee_rate)
934928 . map_err ( |_| MutinyError :: PayjoinConfigError ) ?;
929+ self . spawn_payjoin_sender ( labels, original_psbt, req_ctx)
930+ . await ;
931+ Ok ( ( ) )
932+ }
935933
936- log_debug ! ( self . logger, "Sending payjoin request" ) ;
937- let res = client
938- . post ( req. url )
939- . body ( req. body )
940- . header ( "Content-Type" , "text/plain" )
941- . send ( )
942- . await
943- . map_err ( |_| MutinyError :: PayjoinCreateRequest ) ?
944- . bytes ( )
945- . await
946- . map_err ( |_| MutinyError :: PayjoinCreateRequest ) ?;
934+ async fn spawn_payjoin_sender (
935+ & self ,
936+ labels : Vec < String > ,
937+ original_psbt : bitcoin:: psbt:: Psbt ,
938+ req_ctx : payjoin:: send:: RequestContext ,
939+ ) {
940+ let wallet = self . wallet . clone ( ) ;
941+ let logger = self . logger . clone ( ) ;
942+ let stop = self . stop . clone ( ) ;
943+ utils:: spawn ( async move {
944+ let proposal_psbt = match Self :: poll_payjoin_sender ( stop, req_ctx) . await {
945+ Ok ( psbt) => psbt,
946+ Err ( e) => {
947+ log_error ! ( logger, "Error polling payjoin sender: {e}" ) ;
948+ return ;
949+ }
950+ } ;
947951
948- let mut cursor = Cursor :: new ( res. to_vec ( ) ) ;
952+ if let Err ( e) = Self :: handle_proposal_psbt (
953+ logger. clone ( ) ,
954+ wallet,
955+ original_psbt,
956+ proposal_psbt,
957+ labels,
958+ )
959+ . await
960+ {
961+ log_error ! ( logger, "Error handling payjoin proposal: {e}" ) ;
962+ }
963+ } ) ;
964+ }
949965
950- log_debug ! ( self . logger, "Processing payjoin response" ) ;
951- let proposal_psbt = ctx. process_response ( & mut cursor) . map_err ( |e| {
952- log_error ! ( self . logger, "Error processing payjoin response: {e}" ) ;
953- e
954- } ) ?;
966+ async fn poll_payjoin_sender (
967+ stop : Arc < AtomicBool > ,
968+ req_ctx : payjoin:: send:: RequestContext ,
969+ ) -> Result < bitcoin:: psbt:: Psbt , MutinyError > {
970+ let http = Client :: builder ( )
971+ . build ( )
972+ . map_err ( |_| MutinyError :: Other ( anyhow ! ( "failed to build http client" ) ) ) ?;
973+ loop {
974+ if stop. load ( Ordering :: Relaxed ) {
975+ return Err ( MutinyError :: NotRunning ) ;
976+ }
955977
956- // convert to pdk types
957- let original_psbt = PartiallySignedTransaction :: from_str ( & original_psbt. to_string ( ) )
958- . map_err ( |_| MutinyError :: PayjoinConfigError ) ?;
959- let proposal_psbt = PartiallySignedTransaction :: from_str ( & proposal_psbt. to_string ( ) )
960- . map_err ( |_| MutinyError :: PayjoinConfigError ) ?;
978+ let ( req, ctx) = req_ctx
979+ . extract_v2 ( & crate :: payjoin:: OHTTP_RELAYS [ 0 ] )
980+ . map_err ( |_| MutinyError :: PayjoinConfigError ) ?;
981+ let response = http
982+ . post ( req. url )
983+ . body ( req. body )
984+ . send ( )
985+ . await
986+ . map_err ( |_| MutinyError :: Other ( anyhow ! ( "failed to parse payjoin response" ) ) ) ?;
987+ let mut reader =
988+ std:: io:: Cursor :: new ( response. bytes ( ) . await . map_err ( |_| {
989+ MutinyError :: Other ( anyhow ! ( "failed to parse payjoin response" ) )
990+ } ) ?) ;
991+
992+ println ! ( "Sent fallback transaction" ) ;
993+ let psbt = ctx
994+ . process_response ( & mut reader)
995+ . map_err ( MutinyError :: PayjoinValidateResponse ) ?;
996+ if let Some ( psbt) = psbt {
997+ let psbt = bitcoin:: psbt:: Psbt :: from_str ( & psbt. to_string ( ) )
998+ . map_err ( |_| MutinyError :: Other ( anyhow ! ( "psbt conversion failed" ) ) ) ?;
999+ return Ok ( psbt) ;
1000+ } else {
1001+ log:: info!( "No response yet for POST payjoin request, retrying some seconds" ) ;
1002+ std:: thread:: sleep ( std:: time:: Duration :: from_secs ( 5 ) ) ;
1003+ }
1004+ }
1005+ }
9611006
962- log_debug ! ( self . logger, "Sending payjoin.." ) ;
963- let tx = self
964- . wallet
1007+ async fn handle_proposal_psbt (
1008+ logger : Arc < MutinyLogger > ,
1009+ wallet : Arc < OnChainWallet < S > > ,
1010+ original_psbt : PartiallySignedTransaction ,
1011+ proposal_psbt : PartiallySignedTransaction ,
1012+ labels : Vec < String > ,
1013+ ) -> Result < Txid , MutinyError > {
1014+ log_debug ! ( logger, "Sending payjoin.." ) ;
1015+ let tx = wallet
9651016 . send_payjoin ( original_psbt, proposal_psbt, labels)
9661017 . await ?;
9671018 let txid = tx. txid ( ) ;
968- self . broadcast_transaction ( tx) . await ?;
969- log_debug ! ( self . logger, "Payjoin broadcast! TXID: {txid}" ) ;
1019+ wallet . broadcast_transaction ( tx) . await ?;
1020+ log_info ! ( logger, "Payjoin broadcast! TXID: {txid}" ) ;
9701021 Ok ( txid)
9711022 }
9721023
0 commit comments