Skip to content

Commit

Permalink
Separate receive submodules to be additive (payjoin#466)
Browse files Browse the repository at this point in the history
1. kills the PjUriBuilder pattern which was a feature gating hell
2. make separate receive::{v1,v2} modules to enable error separation and
additive use
3. Pass `supported_versions` from the calling module since that's where
version comes from
4. Explicitly removes non-additive not(feature = "v2") so integration
tests can all run on --all-features

We've got a ways to go to separate modules for readability, and then to
make the features in payjoin-cli additive, but we're getting there.

This is work toward payjoin#392 

This can be followed by separating the error variants into distinct
receive, receive::v1, receive::v2 types and making the v1/v2 features in
payjoin-cli additive
  • Loading branch information
spacebear21 authored Jan 8, 2025
2 parents fc7dc75 + 5c78a15 commit eaf2398
Show file tree
Hide file tree
Showing 11 changed files with 1,215 additions and 1,327 deletions.
19 changes: 10 additions & 9 deletions payjoin-cli/src/app/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ use hyper::{Method, Request, Response, StatusCode};
use hyper_util::rt::TokioIo;
use payjoin::bitcoin::psbt::Psbt;
use payjoin::bitcoin::{self, FeeRate};
use payjoin::receive::{PayjoinProposal, UncheckedProposal};
use payjoin::receive::v1::{PayjoinProposal, UncheckedProposal};
use payjoin::send::v1::SenderBuilder;
use payjoin::{Error, PjUriBuilder, Uri, UriExt};
use payjoin::{Error, Uri, UriExt};
use tokio::net::TcpListener;

use super::config::AppConfig;
Expand All @@ -28,7 +28,7 @@ use crate::db::Database;
pub const LOCAL_CERT_FILE: &str = "localhost.der";

struct Headers<'a>(&'a hyper::HeaderMap);
impl payjoin::receive::Headers for Headers<'_> {
impl payjoin::receive::v1::Headers for Headers<'_> {
fn get_header(&self, key: &str) -> Option<&str> {
self.0.get(key).map(|v| v.to_str()).transpose().ok().flatten()
}
Expand Down Expand Up @@ -137,7 +137,9 @@ impl App {
let pj_part = payjoin::Url::parse(pj_part)
.map_err(|e| anyhow!("Failed to parse pj_endpoint: {}", e))?;

let pj_uri = PjUriBuilder::new(pj_receiver_address, pj_part).amount(amount).build();
let mut pj_uri =
payjoin::receive::v1::build_v1_pj_uri(&pj_receiver_address, &pj_part, false);
pj_uri.amount = Some(amount);

Ok(pj_uri.to_string())
}
Expand Down Expand Up @@ -263,7 +265,7 @@ impl App {
} else {
format!("{}?pj={}", address.to_qr_uri(), self.config.pj_endpoint)
};
let uri = payjoin::Uri::try_from(uri_string.clone())
let uri = Uri::try_from(uri_string.clone())
.map_err(|_| Error::Server(anyhow!("Could not parse payjoin URI string.").into()))?;
let _ = uri.assume_checked(); // we just got it from bitcoind above

Expand All @@ -278,8 +280,7 @@ impl App {
let headers = Headers(&parts.headers);
let query_string = parts.uri.query().unwrap_or("");
let body = body.collect().await.map_err(|e| Error::Server(e.into()))?.aggregate().reader();
let proposal =
payjoin::receive::UncheckedProposal::from_request(body, query_string, headers)?;
let proposal = UncheckedProposal::from_request(body, query_string, headers)?;

let payjoin_proposal = self.process_v1_proposal(proposal)?;
let psbt = payjoin_proposal.psbt();
Expand Down Expand Up @@ -379,9 +380,9 @@ impl App {
}

fn try_contributing_inputs(
payjoin: payjoin::receive::WantsInputs,
payjoin: payjoin::receive::v1::WantsInputs,
bitcoind: &bitcoincore_rpc::Client,
) -> Result<payjoin::receive::ProvisionalProposal> {
) -> Result<payjoin::receive::v1::ProvisionalProposal> {
let candidate_inputs = bitcoind
.list_unspent(None, None, None, None, None)
.context("Failed to list unspent from bitcoind")?
Expand Down
7 changes: 2 additions & 5 deletions payjoin-cli/src/app/v2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,8 @@ impl App {
amount: Option<Amount>,
) -> Result<()> {
println!("Receive session established");
let mut pj_uri_builder = session.pj_uri_builder();
if let Some(amount) = amount {
pj_uri_builder = pj_uri_builder.amount(amount);
}
let pj_uri = pj_uri_builder.build();
let mut pj_uri = session.pj_uri();
pj_uri.amount = amount;

println!("Request Payjoin by sharing this Payjoin Uri:");
println!("{}", pj_uri);
Expand Down
3 changes: 1 addition & 2 deletions payjoin/contrib/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@
set -e

cargo test --locked --package payjoin --verbose --all-features --lib
cargo test --locked --package payjoin --verbose --features=send,receive --test integration
cargo test --locked --package payjoin --verbose --no-default-features --features=send,receive,_danger-local-https,v2,io --test integration
cargo test --locked --package payjoin --verbose --all-features --test integration
2 changes: 1 addition & 1 deletion payjoin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ mod uri;

#[cfg(feature = "base64")]
pub use bitcoin::base64;
pub use uri::{PjParseError, PjUri, PjUriBuilder, Uri, UriExt};
pub use uri::{PjParseError, PjUri, Uri, UriExt};
pub use url::{ParseError, Url};
5 changes: 2 additions & 3 deletions payjoin/src/receive/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,15 @@ impl fmt::Display for RequestError {
format!("Content length too large: {}.", length),
),
InternalRequestError::SenderParams(e) => match e {
super::optional_parameters::Error::UnknownVersion => {
super::optional_parameters::Error::UnknownVersion { supported_versions } => {
write!(
f,
r#"{{
"errorCode": "version-unsupported",
"supported": "{}",
"message": "This version of payjoin is not supported."
}}"#,
serde_json::to_string(&super::optional_parameters::SUPPORTED_VERSIONS)
.map_err(|_| fmt::Error)?
serde_json::to_string(supported_versions).map_err(|_| fmt::Error)?
)
}
_ => write_error(f, "sender-params-error", e),
Expand Down
Loading

0 comments on commit eaf2398

Please sign in to comment.