Skip to content

Commit

Permalink
f Allow node runner to accepts HTTP request on /payjoin endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
jbesraa committed Feb 22, 2024
1 parent 65b23e6 commit 67e887f
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 255 deletions.
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,14 @@ tokio = { version = "1", default-features = false, features = [ "rt-multi-thread
esplora-client = { version = "0.6", default-features = false }
libc = "0.2"
uniffi = { version = "0.26.0", features = ["build"], optional = true }
axum = "0.7.4"
payjoin = { version = "0.13.0", features = ["receive", "send"] }
http-body-util = "0.1.0"
bitcoincore-rpc = "0.17.0"
ureq = "2.9.1"
hyper = {version = "1.2.0", features = ["http1", "server"]}
rustls = "0.21.9"
bytes = "1.5.0"
hyper-util = {version = "0.1.3", features = ["tokio"] }

[target.'cfg(vss)'.dependencies]
vss-client = "0.2"
Expand Down
8 changes: 6 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ pub mod io;
mod liquidity;
mod logger;
mod message_handler;
mod payjoin;
mod payment_store;
mod peer_store;
mod pj;
mod sweep;
mod tx_broadcaster;
mod types;
Expand Down Expand Up @@ -583,6 +583,10 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
});
}

runtime.spawn(async move {
pj::Receiver::start().await.unwrap();
});

// Regularly reconnect to channel peers.
let connect_cm = Arc::clone(&self.channel_manager);
let connect_pm = Arc::clone(&self.peer_manager);
Expand Down Expand Up @@ -800,7 +804,7 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
pub fn payjoin_uri(&self) -> Result<String, Error> {
let address = self.wallet.get_new_address()?;
let amount = Amount::from_sat(1000);
let pj = "https://localhost:3227/payjoin";
let pj = "https://0.0.0.0:3227/payjoin";
let pj_uri_string = format!("{}?amount={}&pj={}", address.to_qr_uri(), amount.to_btc(), pj);
assert!(Uri::from_str(&pj_uri_string).is_ok());
Ok(pj_uri_string)
Expand Down
197 changes: 0 additions & 197 deletions src/payjoin.rs

This file was deleted.

88 changes: 88 additions & 0 deletions src/pj.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
use bitcoin::address::NetworkChecked;
use hyper::HeaderMap;
use payjoin::bitcoin::{self, Amount};
use payjoin::Uri;
use std::str::FromStr;

struct Headers(HeaderMap);

impl payjoin::receive::Headers for Headers {
fn get_header(&self, key: &str) -> Option<&str> {
self.0.get(key).and_then(|v| v.to_str().ok())
}
}

pub struct Receiver;

impl Receiver {
pub async fn start() -> Result<(), Box<dyn std::error::Error>> {
http_server::start().await.unwrap();
Ok(())
}

fn _build_pj_uri(
address: bitcoin::Address, amount: Amount, pj: &'static str,
) -> Result<Uri<'static, NetworkChecked>, Box<dyn std::error::Error>> {
let pj_uri_string = format!("{}?amount={}&pj={}", address.to_qr_uri(), amount.to_btc(), pj);
let pj_uri = Uri::from_str(&pj_uri_string).map_err(|e| e.to_string())?;
Ok(pj_uri.assume_checked())
}
}

mod http_server {
use bytes::Bytes;
use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Method, Request, Response, StatusCode};
use hyper_util::rt::TokioIo;
use std::net::SocketAddr;
use tokio::net::TcpListener;

pub async fn start() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let addr = SocketAddr::from(([127, 0, 0, 1], 3227));
let listener = TcpListener::bind(addr).await?;
println!("Listening on http://{}", addr);
loop {
let (stream, _) = listener.accept().await?;
let io = TokioIo::new(stream);

tokio::task::spawn(async move {
if let Err(err) =
http1::Builder::new().serve_connection(io, service_fn(request_handler)).await
{
println!("Error serving connection: {:?}", err);
}
});
}
}

async fn request_handler(
req: Request<hyper::body::Incoming>,
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
match (req.method(), req.uri().path()) {
// Serve some instructions at /
(&Method::GET, "/") => Ok(Response::new(full(
"Try POSTing data to /request_handler such as: `curl localhost:3000/request_handler -XPOST -d \"PAYJOIN ME\"`",
))),

// Simply echo the body back to the client.
(&Method::POST, "/payjoin") => Ok(Response::new(req.into_body().boxed())),

// Return the 404 Not Found for other routes.
_ => {
let mut not_found = Response::new(
Empty::<Bytes>::new()
.map_err(|never| match never {})
.boxed()
);
*not_found.status_mut() = StatusCode::NOT_FOUND;
Ok(not_found)
}
}
}

fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {
Full::new(chunk.into()).map_err(|never| match never {}).boxed()
}
}
4 changes: 2 additions & 2 deletions src/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,11 @@ where
Ok(self.inner.lock().unwrap().get_balance()?)
}

pub(crate) fn list_unspent(&self) -> Result<Vec<bdk::LocalUtxo>, Error> {
pub(crate) fn _list_unspent(&self) -> Result<Vec<bdk::LocalUtxo>, Error> {
Ok(self.inner.lock().unwrap().list_unspent()?)
}

pub(crate) fn wallet_process_psbt(&self, psbt: &Psbt) -> Result<Psbt, Error> {
pub(crate) fn _wallet_process_psbt(&self, psbt: &Psbt) -> Result<Psbt, Error> {
let wallet = self.inner.lock().unwrap();
let mut psbt = psbt.clone();
wallet.sign(&mut psbt, SignOptions::default())?;
Expand Down

0 comments on commit 67e887f

Please sign in to comment.