Skip to content

Commit

Permalink
Upgrade rustls v0.22.4 (#290)
Browse files Browse the repository at this point in the history
Moving `payjoin-directory` to `hyper-1.+` In
order to get the `rustls-0.22.4` support in `hyper-rustls`

This fixes a bug in payjoin-directory and should be followed by an
updated deployment of that crate.

- first patch applies crate updates
- second patch limits compilation in v2 mode where hyper deps aren't
needed by adding a v1 feature and making some deps optional
  • Loading branch information
DanGould authored Aug 15, 2024
2 parents 515e06a + 09e0638 commit cc97788
Show file tree
Hide file tree
Showing 9 changed files with 575 additions and 436 deletions.
592 changes: 321 additions & 271 deletions Cargo.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions contrib/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ set -e
echo "Running Rust tests..."
cargo test --package payjoin --verbose --all-features --lib
cargo test --package payjoin --verbose --features=send,receive --test integration
cargo test --package payjoin --verbose --features=send,receive,danger-local-https,v2 --test integration
cargo test --package payjoin-cli --verbose --features=danger-local-https,v2 --test e2e
cargo test --package payjoin --verbose --no-default-features --features=send,receive,danger-local-https,v2 --test integration
cargo test --package payjoin-cli --verbose --no-default-features --features=danger-local-https,v2 --test e2e
cargo test --package payjoin-cli --verbose --features=danger-local-https

13 changes: 9 additions & 4 deletions payjoin-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,10 @@ path = "src/main.rs"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[features]
default = ["v1"]
native-certs = ["reqwest/rustls-tls-native-roots"]
danger-local-https = ["rcgen", "reqwest/rustls-tls", "rustls", "hyper-rustls", "payjoin/danger-local-https"]
danger-local-https = ["rcgen", "reqwest/rustls-tls", "rustls", "hyper-rustls", "payjoin/danger-local-https", "tokio-rustls"]
v1 = ["hyper", "hyper-util", "http-body-util"]
v2 = ["payjoin/v2", "payjoin/io"]

[dependencies]
Expand All @@ -31,16 +33,19 @@ bitcoincore-rpc = "0.19.0"
clap = { version = "~4.0.32", features = ["derive"] }
config = "0.13.3"
env_logger = "0.9.0"
hyper = { version = "0.14", features = ["full"] }
hyper-rustls = { version = "0.25", optional = true }
http-body-util = { version = "0.1", optional = true }
hyper = { version = "1", features = ["full"], optional = true }
hyper-rustls = { version = "0.26", optional = true }
hyper-util = { version = "0.1", optional = true }
log = "0.4.7"
payjoin = { version = "0.20.0", features = ["send", "receive", "base64"] }
rcgen = { version = "0.11.1", optional = true }
reqwest = { version = "0.12", default-features = false }
rustls = { version = "0.22.2", optional = true }
rustls = { version = "0.22.4", optional = true }
serde = { version = "1.0.160", features = ["derive"] }
sled = "0.34"
tokio = { version = "1.12.0", features = ["full"] }
tokio-rustls = { version = "0.25", features = ["ring"], default-features = false, optional = true }
url = { version = "2.3.1", features = ["serde"] }

[dev-dependencies]
Expand Down
9 changes: 1 addition & 8 deletions payjoin-cli/src/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use payjoin::{bitcoin, PjUri};
pub mod config;
use crate::app::config::AppConfig;

#[cfg(not(feature = "v2"))]
#[cfg(all(not(feature = "v2"), feature = "v1"))]
pub(crate) mod v1;
#[cfg(feature = "v2")]
pub(crate) mod v2;
Expand Down Expand Up @@ -126,13 +126,6 @@ fn try_contributing_inputs(
Ok(())
}

struct Headers<'a>(&'a hyper::HeaderMap);
impl payjoin::receive::Headers for Headers<'_> {
fn get_header(&self, key: &str) -> Option<&str> {
self.0.get(key).map(|v| v.to_str()).transpose().ok().flatten()
}
}

#[cfg(feature = "danger-local-https")]
fn http_agent() -> Result<reqwest::Client> { Ok(http_agent_builder()?.build()?) }

Expand Down
142 changes: 90 additions & 52 deletions payjoin-cli/src/app/v1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,33 @@ use std::sync::Arc;
use anyhow::{anyhow, Context, Result};
use bitcoincore_rpc::bitcoin::Amount;
use bitcoincore_rpc::RpcApi;
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, Server, StatusCode};
use http_body_util::combinators::BoxBody;
use http_body_util::{BodyExt, Full};
use hyper::body::{Buf, Bytes, Incoming};
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Method, Request, Response, StatusCode};
use hyper_util::rt::TokioIo;
use payjoin::bitcoin::psbt::Psbt;
use payjoin::bitcoin::{self};
use payjoin::receive::{PayjoinProposal, UncheckedProposal};
use payjoin::{Error, PjUriBuilder, Uri, UriExt};
use tokio::net::TcpListener;

use super::config::AppConfig;
use super::App as AppTrait;
use crate::app::{http_agent, try_contributing_inputs, Headers};
use crate::app::{http_agent, try_contributing_inputs};
use crate::db::Database;
#[cfg(feature = "danger-local-https")]
pub const LOCAL_CERT_FILE: &str = "localhost.der";

struct Headers<'a>(&'a hyper::HeaderMap);
impl payjoin::receive::Headers for Headers<'_> {
fn get_header(&self, key: &str) -> Option<&str> {
self.0.get(key).map(|v| v.to_str()).transpose().ok().flatten()
}
}

#[derive(Clone)]
pub(crate) struct App {
config: AppConfig,
Expand Down Expand Up @@ -124,46 +137,66 @@ impl App {

async fn start_http_server(self) -> Result<()> {
let addr = SocketAddr::from(([0, 0, 0, 0], self.config.port));
#[cfg(feature = "danger-local-https")]
let server = {
use std::io::Write;

use hyper::server::conn::AddrIncoming;
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};

let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()])?;
let cert_der = cert.serialize_der()?;
let mut local_cert_path = std::env::temp_dir();
local_cert_path.push(LOCAL_CERT_FILE);
let mut file = std::fs::File::create(local_cert_path)?;
file.write_all(&cert_der)?;
let key =
PrivateKeyDer::from(PrivatePkcs8KeyDer::from(cert.serialize_private_key_der()));
let certs = vec![CertificateDer::from(cert.serialize_der()?)];
let incoming = AddrIncoming::bind(&addr)?;
let acceptor = hyper_rustls::TlsAcceptor::builder()
.with_single_cert(certs, key)
.map_err(|e| anyhow::anyhow!("TLS error: {}", e))?
.with_all_versions_alpn()
.with_incoming(incoming);
Server::builder(acceptor)
};

#[cfg(not(feature = "danger-local-https"))]
let server = Server::bind(&addr);
let listener = TcpListener::bind(addr).await?;
let app = self.clone();
let make_svc = make_service_fn(|_| {

#[cfg(feature = "danger-local-https")]
let tls_acceptor = Self::init_tls_acceptor()?;
while let Ok((stream, _)) = listener.accept().await {
let app = app.clone();
async move {
let handler = move |req| app.clone().handle_web_request(req);
Ok::<_, hyper::Error>(service_fn(handler))
}
});
server.serve(make_svc).await?;
#[cfg(feature = "danger-local-https")]
let tls_acceptor = tls_acceptor.clone();
tokio::spawn(async move {
#[cfg(feature = "danger-local-https")]
let stream = match tls_acceptor.accept(stream).await {
Ok(tls_stream) => tls_stream,
Err(e) => {
log::error!("TLS accept error: {}", e);
return;
}
};

let _ = http1::Builder::new()
.serve_connection(
TokioIo::new(stream),
service_fn(move |req| app.clone().handle_web_request(req)),
)
.await;
});
}
Ok(())
}

async fn handle_web_request(self, req: Request<Body>) -> Result<Response<Body>> {
#[cfg(feature = "danger-local-https")]
fn init_tls_acceptor() -> Result<tokio_rustls::TlsAcceptor> {
use std::io::Write;

use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::ServerConfig;
use tokio_rustls::TlsAcceptor;

let cert = rcgen::generate_simple_self_signed(vec!["localhost".to_string()])?;
let cert_der = cert.serialize_der()?;
let mut local_cert_path = std::env::temp_dir();
local_cert_path.push(LOCAL_CERT_FILE);
let mut file = std::fs::File::create(local_cert_path)?;
file.write_all(&cert_der)?;
let key = PrivateKeyDer::try_from(cert.serialize_private_key_der())
.map_err(|e| anyhow::anyhow!("Could not parse key: {}", e))?;
let certs = vec![CertificateDer::from(cert_der)];
let mut server_config = ServerConfig::builder()
.with_no_client_auth()
.with_single_cert(certs, key)
.map_err(|e| anyhow::anyhow!("TLS error: {}", e))?;
server_config.alpn_protocols =
vec![b"h2".to_vec(), b"http/1.1".to_vec(), b"http/1.0".to_vec()];
Ok(TlsAcceptor::from(Arc::new(server_config)))
}

async fn handle_web_request(
self,
req: Request<Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>> {
log::debug!("Received request: {:?}", req);
let mut response = match (req.method(), req.uri().path()) {
(&Method::GET, "/bip21") => {
Expand All @@ -177,7 +210,7 @@ impl App {
self.handle_get_bip21(amount)
.map_err(|e| {
log::error!("Error handling request: {}", e);
Response::builder().status(500).body(Body::from(e.to_string())).unwrap()
Response::builder().status(500).body(full(e.to_string())).unwrap()
})
.unwrap_or_else(|err_resp| err_resp)
}
Expand All @@ -187,26 +220,26 @@ impl App {
.map_err(|e| match e {
Error::BadRequest(e) => {
log::error!("Error handling request: {}", e);
Response::builder().status(400).body(Body::from(e.to_string())).unwrap()
Response::builder().status(400).body(full(e.to_string())).unwrap()
}
e => {
log::error!("Error handling request: {}", e);
Response::builder().status(500).body(Body::from(e.to_string())).unwrap()
Response::builder().status(500).body(full(e.to_string())).unwrap()
}
})
.unwrap_or_else(|err_resp| err_resp),
_ => Response::builder()
.status(StatusCode::NOT_FOUND)
.body(Body::from("Not found"))
.unwrap(),
_ => Response::builder().status(StatusCode::NOT_FOUND).body(full("Not found")).unwrap(),
};
response
.headers_mut()
.insert("Access-Control-Allow-Origin", hyper::header::HeaderValue::from_static("*"));
Ok(response)
}

fn handle_get_bip21(&self, amount: Option<Amount>) -> Result<Response<Body>, Error> {
fn handle_get_bip21(
&self,
amount: Option<Amount>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, Error> {
let address = self
.bitcoind()
.map_err(|e| Error::Server(e.into()))?
Expand All @@ -227,16 +260,17 @@ impl App {
.map_err(|_| Error::Server(anyhow!("Could not parse payjoin URI string.").into()))?;
let _ = uri.assume_checked(); // we just got it from bitcoind above

Ok(Response::new(Body::from(uri_string)))
Ok(Response::new(full(uri_string)))
}

async fn handle_payjoin_post(&self, req: Request<Body>) -> Result<Response<Body>, Error> {
async fn handle_payjoin_post(
&self,
req: Request<Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, Error> {
let (parts, body) = req.into_parts();
let headers = Headers(&parts.headers);
let query_string = parts.uri.query().unwrap_or("");
let body = std::io::Cursor::new(
hyper::body::to_bytes(body).await.map_err(|e| Error::Server(e.into()))?.to_vec(),
);
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)?;

Expand All @@ -247,7 +281,7 @@ impl App {
"Responded with Payjoin proposal {}",
psbt.clone().extract_tx_unchecked_fee_rate().compute_txid()
);
Ok(Response::new(Body::from(body)))
Ok(Response::new(full(body)))
}

fn process_v1_proposal(&self, proposal: UncheckedProposal) -> Result<PayjoinProposal, Error> {
Expand Down Expand Up @@ -332,3 +366,7 @@ impl App {
Ok(payjoin_proposal)
}
}

fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {
Full::new(chunk.into()).map_err(|never| match never {}).boxed()
}
2 changes: 1 addition & 1 deletion payjoin-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use url::Url;
mod app;
mod db;

#[cfg(not(feature = "v2"))]
#[cfg(all(not(feature = "v2"), feature = "v1"))]
use app::v1::App;
#[cfg(feature = "v2")]
use app::v2::App;
Expand Down
11 changes: 7 additions & 4 deletions payjoin-directory/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,21 @@ resolver = "2"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
danger-local-https = ["hyper-rustls", "rustls"]
danger-local-https = ["hyper-rustls", "rustls", "tokio-rustls"]

[dependencies]
anyhow = "1.0.71"
bitcoin = { version = "0.32.2", features = ["base64"] }
bhttp = { version = "=0.5.1", features = ["http"] }
futures = "0.3.17"
hyper = { version = "0.14", features = ["full"] }
hyper-rustls = { version = "0.24", optional = true }
http-body-util = "0.1.2"
hyper = { version = "1" }
hyper-rustls = { version = "0.26", optional = true }
hyper-util = "0.1"
ohttp = "0.5.1"
redis = { version = "0.23.3", features = ["aio", "tokio-comp"] }
rustls = { version = "0.21", optional = true }
rustls = { version = "0.22.4", optional = true }
tokio = { version = "1.12.0", features = ["full"] }
tokio-rustls = { version = "0.25", features = ["ring"], default-features = false, optional = true }
tracing = "0.1.37"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
Loading

0 comments on commit cc97788

Please sign in to comment.