Skip to content

Commit 4e809ff

Browse files
DanGouldbenalleng
authored andcommitted
Unify v2::Sender fields which were repeated
`v2::Sender` `State` structs held the same data beforehand. This led to duplicate fields and a duplicate `fn endpoint()`. This `Sender::endpoint` function may need to be implemented in `payjoin-ffi` as it is not yet.
1 parent 28dca86 commit 4e809ff

3 files changed

Lines changed: 74 additions & 124 deletions

File tree

payjoin/src/core/send/v2/mod.rs

Lines changed: 42 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -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)]
194191
pub 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

198201
impl<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

220228
impl 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

431419
impl 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

Comments
 (0)