Skip to content

Commit

Permalink
Abstract creating v2 GET requests to common pub(crate) fns
Browse files Browse the repository at this point in the history
  • Loading branch information
0xBEEFCAF3 committed Feb 19, 2025
1 parent d469a1f commit aaabe7b
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 88 deletions.
16 changes: 5 additions & 11 deletions payjoin/src/send/multi_party/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::hpke::HpkeError;
use crate::ohttp::OhttpEncapsulationError;
use crate::receive::ImplementationError;
use crate::send::InternalProposalError;
use crate::uri::url_ext::{ParseOhttpKeysParamError, ParseReceiverPubkeyParamError};
use crate::uri::url_ext::ParseReceiverPubkeyParamError;

#[derive(Debug)]
pub struct CreateRequestError(InternalCreateRequestError);
Expand All @@ -16,10 +16,9 @@ pub(crate) enum InternalCreateRequestError {
#[allow(dead_code)]
Expired(std::time::SystemTime),
MissingOhttpConfig,
OhttpEncapsulation(OhttpEncapsulationError),
Hpke(HpkeError),
ParseReceiverPubkeyParam(ParseReceiverPubkeyParamError),
Url(url::ParseError),
V2CreateRequest(crate::send::v2::CreateRequestError),
}

impl From<InternalCreateRequestError> for CreateRequestError {
Expand All @@ -35,10 +34,9 @@ impl std::error::Error for CreateRequestError {
match &self.0 {
InternalCreateRequestError::Expired(_) => None,
InternalCreateRequestError::MissingOhttpConfig => None,
InternalCreateRequestError::OhttpEncapsulation(e) => Some(e),
InternalCreateRequestError::Hpke(e) => Some(e),
InternalCreateRequestError::ParseReceiverPubkeyParam(e) => Some(e),
InternalCreateRequestError::Url(e) => Some(e),
InternalCreateRequestError::V2CreateRequest(e) => Some(e),
}
}
}
Expand All @@ -48,17 +46,15 @@ pub struct FinalizedError(InternalFinalizedError);

#[derive(Debug)]
pub(crate) enum InternalFinalizedError {
CreateRequest(CreateRequestError),
Encapsulation(OhttpEncapsulationError),
Hpke(HpkeError),
ParseOhttp(ParseOhttpKeysParamError),
InvalidSize,
FinalizePsbt(ImplementationError),
MissingResponse,
Psbt(PsbtError),
#[allow(dead_code)]
UnexpectedStatusCode(http::StatusCode),
Proposal(InternalProposalError),
Ohttp(OhttpEncapsulationError),
}

impl From<InternalFinalizedError> for FinalizedError {
Expand All @@ -72,16 +68,14 @@ impl Display for FinalizedError {
impl std::error::Error for FinalizedError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match &self.0 {
InternalFinalizedError::CreateRequest(e) => Some(e),
InternalFinalizedError::Encapsulation(e) => Some(e),
InternalFinalizedError::Hpke(e) => Some(e),
InternalFinalizedError::ParseOhttp(_e) => None,
InternalFinalizedError::InvalidSize => None,
InternalFinalizedError::FinalizePsbt(_e) => None,
InternalFinalizedError::MissingResponse => None,
InternalFinalizedError::Psbt(e) => Some(e),
InternalFinalizedError::UnexpectedStatusCode(_) => None,
InternalFinalizedError::Proposal(e) => Some(e),
InternalFinalizedError::Ohttp(e) => Some(e),
}
}
}
Expand Down
90 changes: 33 additions & 57 deletions payjoin/src/send/multi_party/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use error::{
use serde::{Deserialize, Serialize};
use url::Url;

use super::v2::{self, EncapsulationError, HpkeContext};
use super::v2::{self, extract_request, EncapsulationError, HpkeContext};
use super::{
append_optimisitic_merge_query_param, serialize_url, AdditionalFeeContribution,
BuildSenderError, InternalResult,
Expand All @@ -15,6 +15,7 @@ use crate::hpke::decrypt_message_b;
use crate::ohttp::ohttp_decapsulate;
use crate::receive::ImplementationError;
use crate::send::v2::V2PostContext;
use crate::uri::UrlExt;
use crate::{PjUri, Request};

mod error;
Expand All @@ -39,55 +40,43 @@ impl Sender {
&self,
ohttp_relay: Url,
) -> Result<(Request, PostContext), CreateRequestError> {
use crate::hpke::encrypt_message_a;
use crate::ohttp::ohttp_encapsulate;
use crate::send::PsbtContext;
use crate::uri::UrlExt;
let url = self.0.endpoint().clone();
if let Ok(expiry) = url.exp() {
if std::time::SystemTime::now() > expiry {
return Err(InternalCreateRequestError::Expired(expiry).into());
}
}
let rs = self
.0
.extract_rs_pubkey()
.map_err(InternalCreateRequestError::ParseReceiverPubkeyParam)?;
let mut ohttp_keys = self
.0
.endpoint()
.ohttp()
.map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
let body = serialize_v2_body(
&self.0.v1.psbt,
self.0.v1.disable_output_substitution,
self.0.v1.fee_contribution,
self.0.v1.min_fee_rate,
)?;
let hpke_ctx = HpkeContext::new(rs, &self.0.reply_key);
let body = encrypt_message_a(
let (request, ohttp_ctx) = extract_request(
ohttp_relay,
self.0.reply_key.clone(),
body,
&hpke_ctx.reply_pair.public_key().clone(),
&hpke_ctx.receiver.clone(),
self.0.endpoint().clone(),
rs.clone(),
&mut ohttp_keys,
)
.map_err(InternalCreateRequestError::Hpke)?;
let mut ohttp = self
.0
.v1
.endpoint
.ohttp()
.map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
let (body, ohttp_ctx) = ohttp_encapsulate(&mut ohttp, "POST", url.as_str(), Some(&body))
.map_err(InternalCreateRequestError::OhttpEncapsulation)?;

.map_err(InternalCreateRequestError::V2CreateRequest)?;
let v2_post_ctx = V2PostContext {
endpoint: self.0.endpoint().clone(),
psbt_ctx: PsbtContext {
psbt_ctx: crate::send::PsbtContext {
original_psbt: self.0.v1.psbt.clone(),
disable_output_substitution: self.0.v1.disable_output_substitution,
fee_contribution: self.0.v1.fee_contribution,
payee: self.0.v1.payee.clone(),
min_fee_rate: self.0.v1.min_fee_rate,
},
hpke_ctx,
hpke_ctx: HpkeContext::new(rs, &self.0.reply_key),
ohttp_ctx,
};
Ok((Request::new_v2(&ohttp_relay, &body), PostContext(v2_post_ctx)))
Ok((request, PostContext(v2_post_ctx)))
}
}

Expand Down Expand Up @@ -146,8 +135,8 @@ impl GetContext {
let response_array: &[u8; crate::directory::ENCAPSULATED_MESSAGE_BYTES] =
response.try_into().map_err(|_| InternalFinalizedError::InvalidSize)?;

let response =
ohttp_decapsulate(ohttp_ctx, response_array).map_err(InternalFinalizedError::Encapsulation)?;
let response = ohttp_decapsulate(ohttp_ctx, response_array)
.map_err(InternalFinalizedError::Ohttp)?;
let body = match response.status() {
http::StatusCode::OK => Some(response.body().to_vec()),
http::StatusCode::ACCEPTED => None,
Expand Down Expand Up @@ -188,35 +177,22 @@ impl FinalizeContext {
pub fn extract_req(
&self,
ohttp_relay: Url,
) -> Result<(Request, ohttp::ClientResponse), FinalizedError> {
use crate::hpke::encrypt_message_a;
use crate::ohttp::ohttp_encapsulate;
use crate::uri::UrlExt;

let hpke_ctx = self.hpke_ctx.clone();
let directory_url = self.directory_url.clone();
// TODO: check if request is expired off the directory url

// The query params are not needed for the final request
// The reciever will ignore them. PSBT is all that is needed.
let body = serialize_v2_body(
&self.psbt,
false, // disable output substitution
None, // fee contribution
FeeRate::BROADCAST_MIN,
)
.map_err(InternalFinalizedError::CreateRequest)?;
let body = encrypt_message_a(
) -> Result<(Request, ohttp::ClientResponse), CreateRequestError> {
let reply_key = self.hpke_ctx.reply_pair.secret_key();
let body = serialize_v2_body(&self.psbt, false, None, FeeRate::BROADCAST_MIN)?;
let mut ohttp_keys = self
.directory_url
.ohttp()
.map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
let (request, ohttp_ctx) = extract_request(
ohttp_relay,
reply_key.clone(),
body,
&hpke_ctx.reply_pair.public_key().clone(),
&hpke_ctx.receiver.clone(),
self.directory_url.clone(),
self.hpke_ctx.receiver.clone(),
&mut ohttp_keys,
)
.map_err(InternalFinalizedError::Hpke)?;
let mut ohttp = directory_url.ohttp().map_err(InternalFinalizedError::ParseOhttp)?;
let (body, ohttp_ctx) =
ohttp_encapsulate(&mut ohttp, "POST", directory_url.as_str(), Some(&body))
.map_err(InternalFinalizedError::Encapsulation)?;
let request = Request::new_v2(&ohttp_relay, &body);
.map_err(InternalCreateRequestError::V2CreateRequest)?;
Ok((request, ohttp_ctx))
}

Expand Down
63 changes: 43 additions & 20 deletions payjoin/src/send/v2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
use bitcoin::hashes::{sha256, Hash};
pub use error::{CreateRequestError, EncapsulationError};
use error::{InternalCreateRequestError, InternalEncapsulationError};
use ohttp::ClientResponse;
use serde::{Deserialize, Serialize};
use url::Url;

Expand All @@ -33,7 +34,7 @@ use crate::hpke::{decrypt_message_b, encrypt_message_a, HpkeSecretKey};
use crate::ohttp::{ohttp_decapsulate, ohttp_encapsulate};
use crate::send::v1;
use crate::uri::{ShortId, UrlExt};
use crate::{HpkeKeyPair, HpkePublicKey, PjUri, Request};
use crate::{HpkeKeyPair, HpkePublicKey, OhttpKeys, PjUri, Request};

mod error;

Expand Down Expand Up @@ -138,37 +139,34 @@ impl Sender {
&self,
ohttp_relay: Url,
) -> Result<(Request, V2PostContext), CreateRequestError> {
use crate::hpke::encrypt_message_a;
use crate::ohttp::ohttp_encapsulate;
use crate::send::PsbtContext;
use crate::uri::UrlExt;
if let Ok(expiry) = self.v1.endpoint.exp() {
if std::time::SystemTime::now() > expiry {
return Err(InternalCreateRequestError::Expired(expiry).into());
}
}
let rs = self.extract_rs_pubkey()?;
let url = self.v1.endpoint.clone();

let mut ohttp_keys = self
.v1
.endpoint()
.ohttp()
.map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
let body = serialize_v2_body(
&self.v1.psbt,
self.v1.disable_output_substitution,
self.v1.fee_contribution,
self.v1.min_fee_rate,
)?;
let hpke_ctx = HpkeContext::new(rs, &self.reply_key);
let body = encrypt_message_a(
let (request, ohttp_ctx) = extract_request(
ohttp_relay,
self.reply_key.clone(),
body,
&hpke_ctx.reply_pair.public_key().clone(),
&hpke_ctx.receiver.clone(),
)
.map_err(InternalCreateRequestError::Hpke)?;
let mut ohttp =
self.v1.endpoint.ohttp().map_err(|_| InternalCreateRequestError::MissingOhttpConfig)?;
let (body, ohttp_ctx) = ohttp_encapsulate(&mut ohttp, "POST", url.as_str(), Some(&body))
.map_err(InternalCreateRequestError::OhttpEncapsulation)?;
log::debug!("ohttp_relay_url: {:?}", ohttp_relay);
self.v1.endpoint.clone(),
self.extract_rs_pubkey()?,
&mut ohttp_keys,
)?;
let rs = self.extract_rs_pubkey()?;
Ok((
Request::new_v2(&ohttp_relay, &body),
request,
V2PostContext {
endpoint: self.v1.endpoint.clone(),
psbt_ctx: PsbtContext {
Expand All @@ -178,7 +176,7 @@ impl Sender {
payee: self.v1.payee.clone(),
min_fee_rate: self.v1.min_fee_rate,
},
hpke_ctx,
hpke_ctx: HpkeContext::new(rs, &self.reply_key),
ohttp_ctx,
},
))
Expand All @@ -193,6 +191,31 @@ impl Sender {
pub fn endpoint(&self) -> &Url { self.v1.endpoint() }
}

pub(crate) fn extract_request(
ohttp_relay: Url,
reply_key: HpkeSecretKey,
body: Vec<u8>,
url: Url,
receiver_pubkey: HpkePublicKey,
ohttp_keys: &mut OhttpKeys,
) -> Result<(Request, ClientResponse), CreateRequestError> {
use crate::hpke::encrypt_message_a;
use crate::ohttp::ohttp_encapsulate;
let hpke_ctx = HpkeContext::new(receiver_pubkey, &reply_key);
let body = encrypt_message_a(
body,
&hpke_ctx.reply_pair.public_key().clone(),
&hpke_ctx.receiver.clone(),
)
.map_err(InternalCreateRequestError::Hpke)?;

let (body, ohttp_ctx) = ohttp_encapsulate(ohttp_keys, "POST", url.as_str(), Some(&body))
.map_err(InternalCreateRequestError::OhttpEncapsulation)?;
log::debug!("ohttp_relay_url: {:?}", ohttp_relay);
let request = Request::new_v2(&ohttp_relay, &body);
Ok((request, ohttp_ctx))
}

pub(crate) fn serialize_v2_body(
psbt: &Psbt,
disable_output_substitution: bool,
Expand Down

0 comments on commit aaabe7b

Please sign in to comment.