@@ -30,11 +30,16 @@ use crate::db::v2::{ReceiverPersister, SenderPersister, SessionId};
3030use crate :: db:: Database ;
3131
3232mod ohttp;
33+ mod schedule;
3334
3435const W_ID : usize = 12 ;
3536const W_ROLE : usize = 25 ;
3637const W_DONE : usize = 15 ;
3738const W_STATUS : usize = 15 ;
39+ // Mean gap of the Poisson poll schedule. The directory learns only this rate,
40+ // so it must stay uniform across clients, not a per-user knob.
41+ const POLL_MEAN : std:: time:: Duration = std:: time:: Duration :: from_secs ( 5 ) ;
42+ const POLL_TIMEOUT : std:: time:: Duration = std:: time:: Duration :: from_secs ( 30 ) ;
3843
3944#[ derive( Clone ) ]
4045pub ( crate ) struct App {
@@ -704,27 +709,44 @@ impl App {
704709 sender : Sender < PollingForProposal > ,
705710 persister : & SenderPersister ,
706711 ) -> Result < ( ) > {
707- let mut session = sender. clone ( ) ;
708- // Long poll until we get a response
712+ let session = sender;
713+ let mut schedule = schedule:: Poisson :: new ( POLL_MEAN ) ;
714+ let mut polls = tokio:: task:: JoinSet :: new ( ) ;
715+ let next = tokio:: time:: sleep ( schedule. next_gap ( ) ) ;
716+ tokio:: pin!( next) ;
709717 loop {
710- let ( response, ctx) =
711- self . post_via_relay ( |relay| session. create_poll_request ( relay) ) . await ?;
712- let res = session. process_response ( & response. bytes ( ) . await ?, ctx) . save ( persister) ;
713- match res {
714- Ok ( OptionalTransitionOutcome :: Progress ( psbt) ) => {
715- println ! ( "Proposal received. Processing..." ) ;
716- self . process_pj_response ( psbt) ?;
717- return Ok ( ( ) ) ;
718- }
719- Ok ( OptionalTransitionOutcome :: Stasis ( current_state) ) => {
720- println ! ( "No response yet." ) ;
721- session = current_state;
722- continue ;
718+ tokio:: select! {
719+ Some ( joined) = polls. join_next( ) , if !polls. is_empty( ) => {
720+ let ( body, ctx) : ( Vec <u8 >, _) = match joined {
721+ Ok ( Ok ( v) ) => v,
722+ _ => continue ,
723+ } ;
724+ match session. clone( ) . process_response( & body, ctx) . save( persister) {
725+ Ok ( OptionalTransitionOutcome :: Progress ( psbt) ) => {
726+ println!( "Proposal received. Processing..." ) ;
727+ self . process_pj_response( psbt) ?;
728+ return Ok ( ( ) ) ;
729+ }
730+ Ok ( OptionalTransitionOutcome :: Stasis ( _) ) => {
731+ println!( "No response yet." ) ;
732+ }
733+ Err ( re) => {
734+ println!( "{re}" ) ;
735+ tracing:: debug!( "{re:?}" ) ;
736+ return Err ( anyhow!( "Response error" ) . context( re) ) ;
737+ }
738+ }
723739 }
724- Err ( re) => {
725- println ! ( "{re}" ) ;
726- tracing:: debug!( "{re:?}" ) ;
727- return Err ( anyhow ! ( "Response error" ) . context ( re) ) ;
740+ ( ) = & mut next => {
741+ next. as_mut( ) . reset( tokio:: time:: Instant :: now( ) + schedule. next_gap( ) ) ;
742+ let relay = self . relay_manager. choose_relay( ) ?;
743+ let ( req, ctx) = session. create_poll_request( relay. as_str( ) ) ?;
744+ let app = self . clone( ) ;
745+ polls. spawn( async move {
746+ let resp =
747+ tokio:: time:: timeout( POLL_TIMEOUT , app. post_request( req) ) . await ??;
748+ Ok :: <_, anyhow:: Error >( ( resp. bytes( ) . await ?. to_vec( ) , ctx) )
749+ } ) ;
728750 }
729751 }
730752 }
@@ -735,24 +757,37 @@ impl App {
735757 session : Receiver < Initialized > ,
736758 persister : & ReceiverPersister ,
737759 ) -> Result < Receiver < UncheckedOriginalPayload > > {
738- let mut session = session;
760+ let mut schedule = schedule:: Poisson :: new ( POLL_MEAN ) ;
761+ let mut polls = tokio:: task:: JoinSet :: new ( ) ;
762+ let next = tokio:: time:: sleep ( schedule. next_gap ( ) ) ;
763+ tokio:: pin!( next) ;
739764 loop {
740- println ! ( "Polling receive request..." ) ;
741- let ( ohttp_response, context) =
742- self . post_via_relay ( |relay| session. create_poll_request ( relay) ) . await ?;
743- let state_transition = session
744- . process_response ( ohttp_response. bytes ( ) . await ?. to_vec ( ) . as_slice ( ) , context)
745- . save ( persister) ;
746- match state_transition {
747- Ok ( OptionalTransitionOutcome :: Progress ( next_state) ) => {
748- println ! ( "Got a request from the sender. Responding with a Payjoin proposal." ) ;
749- return Ok ( next_state) ;
765+ tokio:: select! {
766+ Some ( joined) = polls. join_next( ) , if !polls. is_empty( ) => {
767+ let ( body, ctx) : ( Vec <u8 >, _) = match joined {
768+ Ok ( Ok ( v) ) => v,
769+ _ => continue ,
770+ } ;
771+ match session. clone( ) . process_response( & body, ctx) . save( persister) {
772+ Ok ( OptionalTransitionOutcome :: Progress ( next_state) ) => {
773+ println!( "Got a request from the sender. Responding with a Payjoin proposal." ) ;
774+ return Ok ( next_state) ;
775+ }
776+ Ok ( OptionalTransitionOutcome :: Stasis ( _) ) => { }
777+ Err ( e) => return Err ( e. into( ) ) ,
778+ }
750779 }
751- Ok ( OptionalTransitionOutcome :: Stasis ( current_state) ) => {
752- session = current_state;
753- continue ;
780+ ( ) = & mut next => {
781+ next. as_mut( ) . reset( tokio:: time:: Instant :: now( ) + schedule. next_gap( ) ) ;
782+ let relay = self . relay_manager. choose_relay( ) ?;
783+ let ( req, ctx) = session. create_poll_request( relay. as_str( ) ) ?;
784+ let app = self . clone( ) ;
785+ polls. spawn( async move {
786+ let resp =
787+ tokio:: time:: timeout( POLL_TIMEOUT , app. post_request( req) ) . await ??;
788+ Ok :: <_, anyhow:: Error >( ( resp. bytes( ) . await ?. to_vec( ) , ctx) )
789+ } ) ;
754790 }
755- Err ( e) => return Err ( e. into ( ) ) ,
756791 }
757792 }
758793 }
0 commit comments