Skip to content

Commit 16aadbd

Browse files
committed
Abstract creating v2 POST requests to common pub(crate) fns
Extracting the POST request to read from the pj directory is duplicated in both the v2 sender and multiparty (ns1r) sender. This work introduces a common utility method `create_request` used in both mods.
1 parent 28a9992 commit 16aadbd

File tree

3 files changed

+81
-88
lines changed

3 files changed

+81
-88
lines changed

payjoin/src/send/multiparty/error.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::hpke::HpkeError;
66
use crate::ohttp::OhttpEncapsulationError;
77
use crate::receive::ImplementationError;
88
use crate::send::InternalProposalError;
9-
use crate::uri::url_ext::{ParseOhttpKeysParamError, ParseReceiverPubkeyParamError};
9+
use crate::uri::url_ext::ParseReceiverPubkeyParamError;
1010

1111
#[derive(Debug)]
1212
pub struct CreateRequestError(InternalCreateRequestError);
@@ -16,10 +16,9 @@ pub(crate) enum InternalCreateRequestError {
1616
#[allow(dead_code)]
1717
Expired(std::time::SystemTime),
1818
MissingOhttpConfig,
19-
OhttpEncapsulation(OhttpEncapsulationError),
20-
Hpke(HpkeError),
2119
ParseReceiverPubkeyParam(ParseReceiverPubkeyParamError),
2220
Url(url::ParseError),
21+
V2CreateRequest(crate::send::v2::CreateRequestError),
2322
}
2423

2524
impl From<InternalCreateRequestError> for CreateRequestError {
@@ -35,10 +34,9 @@ impl std::error::Error for CreateRequestError {
3534
match &self.0 {
3635
InternalCreateRequestError::Expired(_) => None,
3736
InternalCreateRequestError::MissingOhttpConfig => None,
38-
InternalCreateRequestError::OhttpEncapsulation(e) => Some(e),
39-
InternalCreateRequestError::Hpke(e) => Some(e),
4037
InternalCreateRequestError::ParseReceiverPubkeyParam(e) => Some(e),
4138
InternalCreateRequestError::Url(e) => Some(e),
39+
InternalCreateRequestError::V2CreateRequest(e) => Some(e),
4240
}
4341
}
4442
}
@@ -48,10 +46,7 @@ pub struct FinalizedError(InternalFinalizedError);
4846

4947
#[derive(Debug)]
5048
pub(crate) enum InternalFinalizedError {
51-
CreateRequest(CreateRequestError),
52-
Encapsulation(OhttpEncapsulationError),
5349
Hpke(HpkeError),
54-
ParseOhttp(ParseOhttpKeysParamError),
5550
InvalidSize,
5651
#[allow(dead_code)]
5752
FinalizePsbt(ImplementationError),
@@ -60,6 +55,7 @@ pub(crate) enum InternalFinalizedError {
6055
#[allow(dead_code)]
6156
UnexpectedStatusCode(http::StatusCode),
6257
Proposal(InternalProposalError),
58+
Ohttp(OhttpEncapsulationError),
6359
}
6460

6561
impl From<InternalFinalizedError> for FinalizedError {
@@ -73,16 +69,14 @@ impl Display for FinalizedError {
7369
impl std::error::Error for FinalizedError {
7470
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
7571
match &self.0 {
76-
InternalFinalizedError::CreateRequest(e) => Some(e),
77-
InternalFinalizedError::Encapsulation(e) => Some(e),
7872
InternalFinalizedError::Hpke(e) => Some(e),
79-
InternalFinalizedError::ParseOhttp(_e) => None,
8073
InternalFinalizedError::InvalidSize => None,
8174
InternalFinalizedError::FinalizePsbt(_) => None,
8275
InternalFinalizedError::MissingResponse => None,
8376
InternalFinalizedError::Psbt(e) => Some(e),
8477
InternalFinalizedError::UnexpectedStatusCode(_) => None,
8578
InternalFinalizedError::Proposal(e) => Some(e),
79+
InternalFinalizedError::Ohttp(e) => Some(e),
8680
}
8781
}
8882
}

payjoin/src/send/multiparty/mod.rs

Lines changed: 33 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ use error::{
66
use serde::{Deserialize, Serialize};
77
use url::Url;
88

9-
use super::v2::{self, EncapsulationError, HpkeContext};
9+
use super::v2::{self, extract_request, EncapsulationError, HpkeContext};
1010
use super::{serialize_url, AdditionalFeeContribution, BuildSenderError, InternalResult};
1111
use crate::hpke::decrypt_message_b;
1212
use crate::ohttp::ohttp_decapsulate;
1313
use crate::receive::ImplementationError;
1414
use crate::send::v2::V2PostContext;
15+
use crate::uri::UrlExt;
1516
use crate::{PjUri, Request};
1617

1718
mod error;
@@ -36,55 +37,43 @@ impl Sender {
3637
&self,
3738
ohttp_relay: Url,
3839
) -> Result<(Request, PostContext), CreateRequestError> {
39-
use crate::hpke::encrypt_message_a;
40-
use crate::ohttp::ohttp_encapsulate;
41-
use crate::send::PsbtContext;
42-
use crate::uri::UrlExt;
43-
let url = self.0.endpoint().clone();
44-
if let Ok(expiry) = url.exp() {
45-
if std::time::SystemTime::now() > expiry {
46-
return Err(InternalCreateRequestError::Expired(expiry).into());
47-
}
48-
}
4940
let rs = self
5041
.0
5142
.extract_rs_pubkey()
5243
.map_err(InternalCreateRequestError::ParseReceiverPubkeyParam)?;
44+
let mut ohttp_keys = self
45+
.0
46+
.endpoint()
47+
.ohttp()
48+
.map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
5349
let body = serialize_v2_body(
5450
&self.0.v1.psbt,
5551
self.0.v1.disable_output_substitution,
5652
self.0.v1.fee_contribution,
5753
self.0.v1.min_fee_rate,
5854
)?;
59-
let hpke_ctx = HpkeContext::new(rs, &self.0.reply_key);
60-
let body = encrypt_message_a(
55+
let (request, ohttp_ctx) = extract_request(
56+
ohttp_relay,
57+
self.0.reply_key.clone(),
6158
body,
62-
&hpke_ctx.reply_pair.public_key().clone(),
63-
&hpke_ctx.receiver.clone(),
59+
self.0.endpoint().clone(),
60+
rs.clone(),
61+
&mut ohttp_keys,
6462
)
65-
.map_err(InternalCreateRequestError::Hpke)?;
66-
let mut ohttp = self
67-
.0
68-
.v1
69-
.endpoint
70-
.ohttp()
71-
.map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
72-
let (body, ohttp_ctx) = ohttp_encapsulate(&mut ohttp, "POST", url.as_str(), Some(&body))
73-
.map_err(InternalCreateRequestError::OhttpEncapsulation)?;
74-
63+
.map_err(InternalCreateRequestError::V2CreateRequest)?;
7564
let v2_post_ctx = V2PostContext {
7665
endpoint: self.0.endpoint().clone(),
77-
psbt_ctx: PsbtContext {
66+
psbt_ctx: crate::send::PsbtContext {
7867
original_psbt: self.0.v1.psbt.clone(),
7968
disable_output_substitution: self.0.v1.disable_output_substitution,
8069
fee_contribution: self.0.v1.fee_contribution,
8170
payee: self.0.v1.payee.clone(),
8271
min_fee_rate: self.0.v1.min_fee_rate,
8372
},
84-
hpke_ctx,
73+
hpke_ctx: HpkeContext::new(rs, &self.0.reply_key),
8574
ohttp_ctx,
8675
};
87-
Ok((Request::new_v2(&ohttp_relay, &body), PostContext(v2_post_ctx)))
76+
Ok((request, PostContext(v2_post_ctx)))
8877
}
8978
}
9079

@@ -143,8 +132,8 @@ impl GetContext {
143132
let response_array: &[u8; crate::directory::ENCAPSULATED_MESSAGE_BYTES] =
144133
response.try_into().map_err(|_| InternalFinalizedError::InvalidSize)?;
145134

146-
let response = ohttp_decapsulate(ohttp_ctx, response_array)
147-
.map_err(InternalFinalizedError::Encapsulation)?;
135+
let response =
136+
ohttp_decapsulate(ohttp_ctx, response_array).map_err(InternalFinalizedError::Ohttp)?;
148137
let body = match response.status() {
149138
http::StatusCode::OK => Some(response.body().to_vec()),
150139
http::StatusCode::ACCEPTED => None,
@@ -185,35 +174,22 @@ impl FinalizeContext {
185174
pub fn extract_req(
186175
&self,
187176
ohttp_relay: Url,
188-
) -> Result<(Request, ohttp::ClientResponse), FinalizedError> {
189-
use crate::hpke::encrypt_message_a;
190-
use crate::ohttp::ohttp_encapsulate;
191-
use crate::uri::UrlExt;
192-
193-
let hpke_ctx = self.hpke_ctx.clone();
194-
let directory_url = self.directory_url.clone();
195-
// TODO: check if request is expired off the directory url
196-
197-
// The query params are not needed for the final request
198-
// The reciever will ignore them. PSBT is all that is needed.
199-
let body = serialize_v2_body(
200-
&self.psbt,
201-
false, // disable output substitution
202-
None, // fee contribution
203-
FeeRate::BROADCAST_MIN,
204-
)
205-
.map_err(InternalFinalizedError::CreateRequest)?;
206-
let body = encrypt_message_a(
177+
) -> Result<(Request, ohttp::ClientResponse), CreateRequestError> {
178+
let reply_key = self.hpke_ctx.reply_pair.secret_key();
179+
let body = serialize_v2_body(&self.psbt, false, None, FeeRate::BROADCAST_MIN)?;
180+
let mut ohttp_keys = self
181+
.directory_url
182+
.ohttp()
183+
.map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
184+
let (request, ohttp_ctx) = extract_request(
185+
ohttp_relay,
186+
reply_key.clone(),
207187
body,
208-
&hpke_ctx.reply_pair.public_key().clone(),
209-
&hpke_ctx.receiver.clone(),
188+
self.directory_url.clone(),
189+
self.hpke_ctx.receiver.clone(),
190+
&mut ohttp_keys,
210191
)
211-
.map_err(InternalFinalizedError::Hpke)?;
212-
let mut ohttp = directory_url.ohttp().map_err(InternalFinalizedError::ParseOhttp)?;
213-
let (body, ohttp_ctx) =
214-
ohttp_encapsulate(&mut ohttp, "POST", directory_url.as_str(), Some(&body))
215-
.map_err(InternalFinalizedError::Encapsulation)?;
216-
let request = Request::new_v2(&ohttp_relay, &body);
192+
.map_err(InternalCreateRequestError::V2CreateRequest)?;
217193
Ok((request, ohttp_ctx))
218194
}
219195

payjoin/src/send/v2/mod.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use bitcoin::hashes::{sha256, Hash};
2525
pub use error::{CreateRequestError, EncapsulationError};
2626
use error::{InternalCreateRequestError, InternalEncapsulationError};
27+
use ohttp::ClientResponse;
2728
use serde::{Deserialize, Serialize};
2829
use url::Url;
2930

@@ -33,7 +34,7 @@ use crate::hpke::{decrypt_message_b, encrypt_message_a, HpkeSecretKey};
3334
use crate::ohttp::{ohttp_decapsulate, ohttp_encapsulate};
3435
use crate::send::v1;
3536
use crate::uri::{ShortId, UrlExt};
36-
use crate::{HpkeKeyPair, HpkePublicKey, IntoUrl, PjUri, Request};
37+
use crate::{HpkeKeyPair, HpkePublicKey, IntoUrl, OhttpKeys, PjUri, Request};
3738

3839
mod error;
3940

@@ -138,37 +139,34 @@ impl Sender {
138139
&self,
139140
ohttp_relay: Url,
140141
) -> Result<(Request, V2PostContext), CreateRequestError> {
141-
use crate::hpke::encrypt_message_a;
142-
use crate::ohttp::ohttp_encapsulate;
143-
use crate::send::PsbtContext;
144-
use crate::uri::UrlExt;
145142
if let Ok(expiry) = self.v1.endpoint.exp() {
146143
if std::time::SystemTime::now() > expiry {
147144
return Err(InternalCreateRequestError::Expired(expiry).into());
148145
}
149146
}
150-
let rs = self.extract_rs_pubkey()?;
151-
let url = self.v1.endpoint.clone();
147+
148+
let mut ohttp_keys = self
149+
.v1
150+
.endpoint()
151+
.ohttp()
152+
.map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
152153
let body = serialize_v2_body(
153154
&self.v1.psbt,
154155
self.v1.disable_output_substitution,
155156
self.v1.fee_contribution,
156157
self.v1.min_fee_rate,
157158
)?;
158-
let hpke_ctx = HpkeContext::new(rs, &self.reply_key);
159-
let body = encrypt_message_a(
159+
let (request, ohttp_ctx) = extract_request(
160+
ohttp_relay,
161+
self.reply_key.clone(),
160162
body,
161-
&hpke_ctx.reply_pair.public_key().clone(),
162-
&hpke_ctx.receiver.clone(),
163-
)
164-
.map_err(InternalCreateRequestError::Hpke)?;
165-
let mut ohttp =
166-
self.v1.endpoint.ohttp().map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
167-
let (body, ohttp_ctx) = ohttp_encapsulate(&mut ohttp, "POST", url.as_str(), Some(&body))
168-
.map_err(InternalCreateRequestError::OhttpEncapsulation)?;
169-
log::debug!("ohttp_relay_url: {:?}", ohttp_relay);
163+
self.v1.endpoint.clone(),
164+
self.extract_rs_pubkey()?,
165+
&mut ohttp_keys,
166+
)?;
167+
let rs = self.extract_rs_pubkey()?;
170168
Ok((
171-
Request::new_v2(&ohttp_relay, &body),
169+
request,
172170
V2PostContext {
173171
endpoint: self.v1.endpoint.clone(),
174172
psbt_ctx: PsbtContext {
@@ -178,7 +176,7 @@ impl Sender {
178176
payee: self.v1.payee.clone(),
179177
min_fee_rate: self.v1.min_fee_rate,
180178
},
181-
hpke_ctx,
179+
hpke_ctx: HpkeContext::new(rs, &self.reply_key),
182180
ohttp_ctx,
183181
},
184182
))
@@ -193,6 +191,31 @@ impl Sender {
193191
pub fn endpoint(&self) -> &Url { self.v1.endpoint() }
194192
}
195193

194+
pub(crate) fn extract_request(
195+
ohttp_relay: Url,
196+
reply_key: HpkeSecretKey,
197+
body: Vec<u8>,
198+
url: Url,
199+
receiver_pubkey: HpkePublicKey,
200+
ohttp_keys: &mut OhttpKeys,
201+
) -> Result<(Request, ClientResponse), CreateRequestError> {
202+
use crate::hpke::encrypt_message_a;
203+
use crate::ohttp::ohttp_encapsulate;
204+
let hpke_ctx = HpkeContext::new(receiver_pubkey, &reply_key);
205+
let body = encrypt_message_a(
206+
body,
207+
&hpke_ctx.reply_pair.public_key().clone(),
208+
&hpke_ctx.receiver.clone(),
209+
)
210+
.map_err(InternalCreateRequestError::Hpke)?;
211+
212+
let (body, ohttp_ctx) = ohttp_encapsulate(ohttp_keys, "POST", url.as_str(), Some(&body))
213+
.map_err(InternalCreateRequestError::OhttpEncapsulation)?;
214+
log::debug!("ohttp_relay_url: {:?}", ohttp_relay);
215+
let request = Request::new_v2(&ohttp_relay, &body);
216+
Ok((request, ohttp_ctx))
217+
}
218+
196219
pub(crate) fn serialize_v2_body(
197220
psbt: &Psbt,
198221
disable_output_substitution: bool,

0 commit comments

Comments
 (0)