@@ -898,75 +898,126 @@ impl<S: MutinyStorage> NodeManager<S> {
898
898
Ok ( enroller. process_res ( ohttp_response. as_ref ( ) , context) ?)
899
899
}
900
900
901
- // Send v1 payjoin request
901
+ // Send v2 payjoin request
902
902
pub async fn send_payjoin (
903
903
& self ,
904
904
uri : Uri < ' _ , payjoin:: bitcoin:: address:: NetworkChecked > ,
905
905
amount : u64 ,
906
906
labels : Vec < String > ,
907
907
fee_rate : Option < f32 > ,
908
- ) -> Result < Txid , MutinyError > {
908
+ ) -> Result < ( ) , MutinyError > {
909
909
let address = Address :: from_str ( & uri. address . to_string ( ) )
910
910
. map_err ( |_| MutinyError :: PayjoinConfigError ) ?;
911
911
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.
913
913
let fee_rate = if let Some ( rate) = fee_rate {
914
914
FeeRate :: from_sat_per_vb ( rate)
915
915
} else {
916
916
let sat_per_kwu = self . fee_estimator . get_normal_fee_rate ( ) ;
917
917
FeeRate :: from_sat_per_kwu ( sat_per_kwu as f32 )
918
918
} ;
919
919
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 (
921
921
& original_psbt. to_string ( ) ,
922
922
)
923
923
. map_err ( |_| MutinyError :: PayjoinConfigError ) ?;
924
924
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)
934
928
. map_err ( |_| MutinyError :: PayjoinConfigError ) ?;
929
+ self . spawn_payjoin_sender ( labels, original_psbt, req_ctx)
930
+ . await ;
931
+ Ok ( ( ) )
932
+ }
935
933
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
+ } ;
947
951
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
+ }
949
965
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
+ }
955
977
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
+ }
961
1006
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
965
1016
. send_payjoin ( original_psbt, proposal_psbt, labels)
966
1017
. await ?;
967
1018
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}" ) ;
970
1021
Ok ( txid)
971
1022
}
972
1023
0 commit comments