@@ -169,11 +169,8 @@ impl SenderBuilder {
169169 pj_param : PjParam ,
170170 psbt_ctx : PsbtContext ,
171171 ) -> NextStateTransition < SessionEvent , Sender < WithReplyKey > > {
172- let with_reply_key = WithReplyKey :: new ( pj_param, psbt_ctx) ;
173- NextStateTransition :: success (
174- SessionEvent :: Created ( Box :: new ( with_reply_key. clone ( ) ) ) ,
175- Sender { state : with_reply_key } ,
176- )
172+ let sender = Sender :: new ( pj_param, psbt_ctx) ;
173+ NextStateTransition :: success ( SessionEvent :: Created ( Box :: new ( sender. clone ( ) ) ) , sender)
177174 }
178175}
179176
@@ -193,6 +190,12 @@ pub trait State: sealed::State {}
193190#[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
194191pub struct Sender < State > {
195192 pub ( crate ) state : State ,
193+ /// The endpoint in the Payjoin URI
194+ pub ( crate ) pj_param : PjParam ,
195+ /// The Original PSBT context
196+ pub ( crate ) psbt_ctx : PsbtContext ,
197+ /// The secret key to decrypt the receiver's reply.
198+ pub ( crate ) reply_key : HpkeSecretKey ,
196199}
197200
198201impl < State > core:: ops:: Deref for Sender < State > {
@@ -205,6 +208,11 @@ impl<State> core::ops::DerefMut for Sender<State> {
205208 fn deref_mut ( & mut self ) -> & mut Self :: Target { & mut self . state }
206209}
207210
211+ impl < State > Sender < State > {
212+ /// The endpoint in the Payjoin URI
213+ pub fn endpoint ( & self ) -> String { self . pj_param . endpoint ( ) . to_string ( ) }
214+ }
215+
208216/// Represents the various states of a Payjoin send session during the protocol flow.
209217///
210218/// This provides type erasure for the send session state, allowing the session to be replayed
@@ -218,7 +226,7 @@ pub enum SendSession {
218226}
219227
220228impl SendSession {
221- fn new ( context : WithReplyKey ) -> Self { SendSession :: WithReplyKey ( Sender { state : context } ) }
229+ fn new ( sender : Sender < WithReplyKey > ) -> Self { SendSession :: WithReplyKey ( sender ) }
222230
223231 fn process_event (
224232 self ,
@@ -244,22 +252,13 @@ impl SendSession {
244252/// A payjoin V2 sender, allowing the construction of a payjoin V2 request
245253/// and the resulting [`V2PostContext`].
246254#[ derive( Debug , Clone , PartialEq , Eq , Serialize , Deserialize ) ]
247- pub struct WithReplyKey {
248- /// The endpoint in the Payjoin URI
249- pub ( crate ) pj_param : PjParam ,
250- /// The Original PSBT context
251- pub ( crate ) psbt_ctx : PsbtContext ,
252- /// The secret key to decrypt the receiver's reply.
253- pub ( crate ) reply_key : HpkeSecretKey ,
254- }
255+ pub struct WithReplyKey ;
255256
256- impl WithReplyKey {
257+ impl Sender < WithReplyKey > {
257258 fn new ( pj_param : PjParam , psbt_ctx : PsbtContext ) -> Self {
258- Self { pj_param, psbt_ctx, reply_key : HpkeKeyPair :: gen_keypair ( ) . 0 }
259+ Sender { state : WithReplyKey , pj_param, psbt_ctx, reply_key : HpkeKeyPair :: gen_keypair ( ) . 0 }
259260 }
260- }
261261
262- impl Sender < WithReplyKey > {
263262 /// Construct serialized Request and Context from a Payjoin Proposal.
264263 ///
265264 /// Important: This request must not be retried or reused on failure.
@@ -336,27 +335,21 @@ impl Sender<WithReplyKey> {
336335 } ,
337336 }
338337
339- let polling_for_proposal = PollingForProposal {
338+ let sender = Sender {
339+ state : PollingForProposal ,
340340 pj_param : post_ctx. pj_param ,
341341 psbt_ctx : post_ctx. psbt_ctx ,
342342 reply_key : post_ctx. reply_key ,
343343 } ;
344- MaybeFatalTransition :: success (
345- SessionEvent :: PostedOriginalPsbt ( ) ,
346- Sender { state : polling_for_proposal } ,
347- )
344+ MaybeFatalTransition :: success ( SessionEvent :: PostedOriginalPsbt ( ) , sender)
348345 }
349346
350- /// The endpoint in the Payjoin URI
351- pub fn endpoint ( & self ) -> String { self . pj_param . endpoint ( ) . to_string ( ) }
352-
353347 pub ( crate ) fn apply_polling_for_proposal ( self ) -> SendSession {
354348 SendSession :: PollingForProposal ( Sender {
355- state : PollingForProposal {
356- pj_param : self . state . pj_param ,
357- psbt_ctx : self . state . psbt_ctx ,
358- reply_key : self . state . reply_key ,
359- } ,
349+ state : PollingForProposal ,
350+ pj_param : self . pj_param ,
351+ psbt_ctx : self . psbt_ctx ,
352+ reply_key : self . reply_key ,
360353 } )
361354 }
362355}
@@ -421,12 +414,7 @@ pub struct V2PostContext {
421414/// This type is used to make a BIP77 GET request and process the response.
422415/// Call [`Sender<PollingForProposal>::process_response`] on it to continue the BIP77 flow.
423416#[ derive( Debug , Clone , Serialize , Deserialize , PartialEq , Eq ) ]
424- pub struct PollingForProposal {
425- /// The endpoint in the Payjoin URI
426- pub ( crate ) pj_param : PjParam ,
427- pub ( crate ) psbt_ctx : PsbtContext ,
428- pub ( crate ) reply_key : HpkeSecretKey ,
429- }
417+ pub struct PollingForProposal ;
430418
431419impl ResponseError {
432420 fn from_slice ( bytes : & [ u8 ] ) -> Result < Self , serde_json:: Error > {
@@ -447,7 +435,7 @@ impl Sender<PollingForProposal> {
447435 & HpkeKeyPair :: from_secret_key ( & self . reply_key ) . public_key ( ) . to_compressed_bytes ( ) ,
448436 ) ;
449437 let mailbox: ShortId = hash. into ( ) ;
450- let url = Url :: parse ( & self . endpoint ( ) )
438+ let url = Url :: parse ( self . pj_param . endpoint ( ) . as_str ( ) )
451439 . expect ( "Could not parse url" )
452440 . join ( & mailbox. to_string ( ) )
453441 . map_err ( |e| InternalCreateRequestError :: Url ( e. into ( ) ) ) ?;
@@ -543,8 +531,6 @@ impl Sender<PollingForProposal> {
543531 SessionEvent :: ReceivedProposalPsbt ( processed_proposal) ,
544532 )
545533 }
546-
547- pub fn endpoint ( & self ) -> String { self . pj_param . endpoint ( ) . to_string ( ) }
548534}
549535
550536#[ cfg( test) ]
@@ -578,17 +564,16 @@ mod test {
578564 HpkeKeyPair :: gen_keypair ( ) . 1 ,
579565 ) ;
580566 Ok ( super :: Sender {
581- state : super :: WithReplyKey {
582- pj_param,
583- psbt_ctx : PsbtContext {
584- original_psbt : PARSED_ORIGINAL_PSBT . clone ( ) ,
585- output_substitution : OutputSubstitution :: Enabled ,
586- fee_contribution : None ,
587- min_fee_rate : FeeRate :: ZERO ,
588- payee : ScriptBuf :: from ( vec ! [ 0x00 ] ) ,
589- } ,
590- reply_key : HpkeKeyPair :: gen_keypair ( ) . 0 ,
567+ state : super :: WithReplyKey ,
568+ pj_param,
569+ psbt_ctx : PsbtContext {
570+ original_psbt : PARSED_ORIGINAL_PSBT . clone ( ) ,
571+ output_substitution : OutputSubstitution :: Enabled ,
572+ fee_contribution : None ,
573+ min_fee_rate : FeeRate :: ZERO ,
574+ payee : ScriptBuf :: from ( vec ! [ 0x00 ] ) ,
591575 } ,
576+ reply_key : HpkeKeyPair :: gen_keypair ( ) . 0 ,
592577 } )
593578 }
594579
@@ -664,33 +649,33 @@ mod test {
664649 . expect ( "sender should succeed" ) ;
665650 // v2 senders may always override the receiver's `pjos` parameter to enable output
666651 // substitution
667- assert_eq ! ( req_ctx. state . psbt_ctx. output_substitution, OutputSubstitution :: Enabled ) ;
668- assert_eq ! ( & req_ctx. state . psbt_ctx. payee, & address. script_pubkey( ) ) ;
652+ assert_eq ! ( req_ctx. psbt_ctx. output_substitution, OutputSubstitution :: Enabled ) ;
653+ assert_eq ! ( & req_ctx. psbt_ctx. payee, & address. script_pubkey( ) ) ;
669654 let fee_contribution =
670- req_ctx. state . psbt_ctx . fee_contribution . expect ( "sender should contribute fees" ) ;
655+ req_ctx. psbt_ctx . fee_contribution . expect ( "sender should contribute fees" ) ;
671656 assert_eq ! ( fee_contribution. max_amount, Amount :: from_sat( 91 ) ) ;
672657 assert_eq ! ( fee_contribution. vout, 0 ) ;
673- assert_eq ! ( req_ctx. state . psbt_ctx. min_fee_rate, FeeRate :: from_sat_per_kwu( 250 ) ) ;
658+ assert_eq ! ( req_ctx. psbt_ctx. min_fee_rate, FeeRate :: from_sat_per_kwu( 250 ) ) ;
674659 // ensure that the other builder methods also enable output substitution
675660 let req_ctx = SenderBuilder :: new ( PARSED_ORIGINAL_PSBT . clone ( ) , pj_uri. clone ( ) )
676661 . build_non_incentivizing ( FeeRate :: BROADCAST_MIN )
677662 . expect ( "build on test vector should succeed" )
678663 . save ( & NoopSessionPersister :: default ( ) )
679664 . expect ( "sender should succeed" ) ;
680- assert_eq ! ( req_ctx. state . psbt_ctx. output_substitution, OutputSubstitution :: Enabled ) ;
665+ assert_eq ! ( req_ctx. psbt_ctx. output_substitution, OutputSubstitution :: Enabled ) ;
681666 let req_ctx = SenderBuilder :: new ( PARSED_ORIGINAL_PSBT . clone ( ) , pj_uri. clone ( ) )
682667 . build_with_additional_fee ( Amount :: ZERO , Some ( 0 ) , FeeRate :: BROADCAST_MIN , false )
683668 . expect ( "build on test vector should succeed" )
684669 . save ( & NoopSessionPersister :: default ( ) )
685670 . expect ( "sender should succeed" ) ;
686- assert_eq ! ( req_ctx. state . psbt_ctx. output_substitution, OutputSubstitution :: Enabled ) ;
671+ assert_eq ! ( req_ctx. psbt_ctx. output_substitution, OutputSubstitution :: Enabled ) ;
687672 // ensure that a v2 sender may still disable output substitution if they prefer.
688673 let req_ctx = SenderBuilder :: new ( PARSED_ORIGINAL_PSBT . clone ( ) , pj_uri)
689674 . always_disable_output_substitution ( )
690675 . build_recommended ( FeeRate :: BROADCAST_MIN )
691676 . expect ( "build on test vector should succeed" )
692677 . save ( & NoopSessionPersister :: default ( ) )
693678 . expect ( "sender should succeed" ) ;
694- assert_eq ! ( req_ctx. state . psbt_ctx. output_substitution, OutputSubstitution :: Disabled ) ;
679+ assert_eq ! ( req_ctx. psbt_ctx. output_substitution, OutputSubstitution :: Disabled ) ;
695680 }
696681}
0 commit comments