Skip to content

Commit e726227

Browse files
committed
f add CreateChannelRequest
1 parent cf50138 commit e726227

File tree

2 files changed

+160
-71
lines changed

2 files changed

+160
-71
lines changed

src/lib.rs

+25-1
Original file line numberDiff line numberDiff line change
@@ -464,8 +464,32 @@ impl<K: KVStore + Sync + Send + 'static> Node<K> {
464464
});
465465
}
466466

467+
use hyper::server::conn::http1;
468+
use hyper_util::rt::TokioIo;
469+
use std::net::SocketAddr;
470+
use tokio::net::TcpListener;
471+
472+
// Start the HTTP server for payjoin
473+
let wallet = Arc::clone(&self.wallet);
467474
runtime.spawn(async move {
468-
pj::Receiver::start().await.unwrap();
475+
let addr = SocketAddr::from(([127, 0, 0, 1], PAYJOIN_HTTP_SERVER_PORT));
476+
let listener = TcpListener::bind(addr).await.unwrap();
477+
dbg!("Started HTTP server on http://{}", addr);
478+
// let our_pubkey= wallet.get_new_address().unwrap().script_pubkey().into_bytes();
479+
let create_channel_request = pj::CreateChannelRequest::init(wallet);
480+
loop {
481+
let (stream, _) = listener.accept().await.unwrap();
482+
let io = TokioIo::new(stream);
483+
let clone_ccr = create_channel_request.clone();
484+
tokio::task::spawn(async move {
485+
if let Err(err) = http1::Builder::new()
486+
.serve_connection(io, clone_ccr)
487+
.await
488+
{
489+
println!("Failed to serve connection: {:?}", err);
490+
}
491+
});
492+
}
469493
});
470494

471495
// Regularly reconnect to channel peers.

src/pj.rs

+135-70
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,153 @@
1-
use bitcoin::address::NetworkChecked;
2-
use hyper::HeaderMap;
3-
use payjoin::bitcoin::{self, Amount};
4-
use payjoin::Uri;
1+
use crate::types::Wallet;
2+
use bitcoin::psbt::Psbt;
3+
use bitcoin::secp256k1::PublicKey;
4+
use bitcoin::TxOut;
5+
use bitcoincore_rpc::jsonrpc::serde_json;
6+
use http_body_util::{BodyExt, Full};
7+
use hyper::body::Incoming as IncomingBody;
8+
use hyper::{service::Service, Request};
59
use std::str::FromStr;
10+
use std::sync::{Arc, Mutex};
11+
use std::{future::Future, pin::Pin};
612

7-
struct Headers(HeaderMap);
8-
9-
impl payjoin::receive::Headers for Headers {
10-
fn get_header(&self, key: &str) -> Option<&str> {
11-
self.0.get(key).and_then(|v| v.to_str().ok())
12-
}
13+
#[derive(Clone)]
14+
pub struct CreateChannelRequest {
15+
wallet: Arc<Wallet>,
16+
channel_amount_sats: Arc<Mutex<Option<u64>>>,
17+
push_msat: Arc<Mutex<Option<u64>>>,
18+
announce_channel: Arc<Mutex<Option<bool>>>,
19+
peer_node_id: Arc<Mutex<Option<PublicKey>>>,
1320
}
1421

15-
pub struct Receiver;
22+
impl CreateChannelRequest {
23+
pub fn init(wallet: Arc<Wallet>) -> Self {
24+
Self {
25+
wallet,
26+
channel_amount_sats: Arc::new(Mutex::new(None)),
27+
push_msat: Arc::new(Mutex::new(None)),
28+
announce_channel: Arc::new(Mutex::new(None)),
29+
peer_node_id: Arc::new(Mutex::new(None)),
30+
}
31+
}
1632

17-
impl Receiver {
18-
pub async fn start() -> Result<(), Box<dyn std::error::Error>> {
19-
http_server::start().await.unwrap();
20-
Ok(())
33+
pub fn set(
34+
&self, channel_amount_sats: u64, push_msat: Option<u64>,
35+
announce_channel: bool, peer_node_id: PublicKey,
36+
) {
37+
*self.channel_amount_sats.lock().unwrap() = Some(channel_amount_sats);
38+
*self.push_msat.lock().unwrap() = push_msat;
39+
*self.announce_channel.lock().unwrap() = Some(announce_channel);
40+
*self.peer_node_id.lock().unwrap() = Some(peer_node_id);
2141
}
2242

23-
fn _build_pj_uri(
24-
address: bitcoin::Address, amount: Amount, pj: &'static str,
25-
) -> Result<Uri<'static, NetworkChecked>, Box<dyn std::error::Error>> {
26-
let pj_uri_string = format!("{}?amount={}&pj={}", address.to_qr_uri(), amount.to_btc(), pj);
27-
let pj_uri = Uri::from_str(&pj_uri_string).map_err(|e| e.to_string())?;
28-
Ok(pj_uri.assume_checked())
43+
pub fn get(&self) -> (u64, Option<u64>, bool, Option<PublicKey>, Arc<Wallet>) {
44+
let channel_amount_sats = self.channel_amount_sats.lock().unwrap().unwrap_or(0);
45+
let push_msat = *self.push_msat.lock().unwrap();
46+
let announce_channel = self.announce_channel.lock().unwrap().unwrap_or(false);
47+
let peer_node_id = if let Some(peer_node_id) = *self.peer_node_id.lock().unwrap() {
48+
Some(peer_node_id)
49+
} else {
50+
None
51+
};
52+
let wallet = self.wallet.clone();
53+
54+
(
55+
channel_amount_sats,
56+
push_msat,
57+
announce_channel,
58+
peer_node_id,
59+
wallet
60+
)
2961
}
3062
}
3163

32-
mod http_server {
33-
use bytes::Bytes;
34-
use http_body_util::{combinators::BoxBody, BodyExt, Empty, Full};
35-
use hyper::server::conn::http1;
36-
use hyper::service::service_fn;
37-
use hyper::{Method, Request, Response, StatusCode};
38-
use hyper_util::rt::TokioIo;
39-
use std::net::SocketAddr;
40-
use tokio::net::TcpListener;
64+
fn make_http_response(s: String) -> Result<hyper::Response<Full<bytes::Bytes>>, hyper::Error> {
65+
Ok(hyper::Response::builder().body(Full::new(bytes::Bytes::from(s))).unwrap())
66+
}
4167

42-
pub async fn start() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
43-
let addr = SocketAddr::from(([127, 0, 0, 1], 3227));
44-
let listener = TcpListener::bind(addr).await?;
45-
println!("Listening on http://{}", addr);
46-
loop {
47-
let (stream, _) = listener.accept().await?;
48-
let io = TokioIo::new(stream);
68+
async fn payjoin_handler(
69+
http_request: Request<hyper::body::Incoming>, chan_req: Arc<Mutex<CreateChannelRequest>>,
70+
) -> Result<hyper::Response<Full<bytes::Bytes>>, hyper::Error> {
71+
match (http_request.method(), http_request.uri().path()) {
72+
(&hyper::Method::POST, "/payjoin") => {
73+
let headers = http_request.headers().clone();
74+
println!("Received payjoin request with headers: {:?}", headers);
75+
let body = http_request.into_body().collect().await?;
76+
let body = String::from_utf8(body.to_bytes().to_vec()).unwrap();
77+
println!("Received payjoin request with body: {:?}", &body);
78+
let psbt = Psbt::from_str(&body).unwrap();
79+
let (channel_amount_sats, push_msat, announce_channel, peer_node_id, wallet) = chan_req.lock().unwrap().get();
80+
let our_output: &TxOut = psbt
81+
.unsigned_tx
82+
.output
83+
.iter()
84+
.find(|out| wallet.is_mine(&out.script_pubkey).unwrap())
85+
.unwrap();
86+
assert!(our_output.value == 1000);
4987

50-
tokio::task::spawn(async move {
51-
if let Err(err) =
52-
http1::Builder::new().serve_connection(io, service_fn(request_handler)).await
53-
{
54-
println!("Error serving connection: {:?}", err);
55-
}
56-
});
57-
}
88+
assert_eq!(channel_amount_sats, 0);
89+
assert_eq!(push_msat, None);
90+
assert_eq!(announce_channel, false);
91+
assert_eq!(peer_node_id, None);
92+
//TODO 1: Validations
93+
//
94+
//TODO 2: Construct + Send OpenChannel MSG
95+
//
96+
//TODO 3: Await for AcceptChannel MSG
97+
//
98+
//TODO 4: Construct + Send FundingCreated MSG (Funding will be created using the above PSBT)
99+
//
100+
//TODO 5: Await for FundingSigned MSG
101+
//
102+
//TODO 6: Construct Final PJ PSBT and respond to sender.
103+
make_http_response(body.into())
104+
},
105+
(&hyper::Method::GET, "/channel_request") => {
106+
let (channel_amount_sats, push_msat, announce_channel, peer_node_id, _) =
107+
chan_req.lock().unwrap().get();
108+
let peer_node_id = if let Some(peer_node_id) = peer_node_id {
109+
peer_node_id.to_string()
110+
} else {
111+
"".to_string()
112+
};
113+
let res = format!(
114+
"{{\"channel_amount_sats\":{},\"push_msat\":{},\"announce_channel\":{},\"peer_node_id\":\"{}\"}}",
115+
channel_amount_sats, push_msat.unwrap_or(0), announce_channel, peer_node_id
116+
);
117+
make_http_response(res)
118+
},
119+
(&hyper::Method::POST, "/channel_request") => {
120+
let body = http_request.into_body().collect().await?;
121+
let body = String::from_utf8(body.to_bytes().to_vec()).unwrap();
122+
let json: serde_json::Value = serde_json::from_str(&body).unwrap();
123+
let channel_amount_sats = json["channel_amount_sats"].as_u64().unwrap();
124+
let push_msat = json["push_msat"].as_u64();
125+
let announce_channel = json["announce_channel"].as_bool().unwrap();
126+
let peer_node_id = PublicKey::from_str(json["peer_node_id"].as_str().unwrap()).unwrap();
127+
chan_req.lock().unwrap().set(
128+
channel_amount_sats,
129+
push_msat,
130+
announce_channel,
131+
peer_node_id
132+
);
133+
make_http_response("{}".into())
134+
},
135+
_ => make_http_response("404".into()),
58136
}
137+
}
59138

60-
async fn request_handler(
61-
req: Request<hyper::body::Incoming>,
62-
) -> Result<Response<BoxBody<Bytes, hyper::Error>>, hyper::Error> {
63-
match (req.method(), req.uri().path()) {
64-
// Serve some instructions at /
65-
(&Method::GET, "/") => Ok(Response::new(full(
66-
"Try POSTing data to /request_handler such as: `curl localhost:3000/request_handler -XPOST -d \"PAYJOIN ME\"`",
67-
))),
68-
69-
// Simply echo the body back to the client.
70-
(&Method::POST, "/payjoin") => Ok(Response::new(req.into_body().boxed())),
139+
impl Service<Request<IncomingBody>> for CreateChannelRequest{
140+
type Response = hyper::Response<Full<bytes::Bytes>>;
141+
type Error = hyper::Error;
142+
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
71143

72-
// Return the 404 Not Found for other routes.
73-
_ => {
74-
let mut not_found = Response::new(
75-
Empty::<Bytes>::new()
76-
.map_err(|never| match never {})
77-
.boxed()
78-
);
79-
*not_found.status_mut() = StatusCode::NOT_FOUND;
80-
Ok(not_found)
81-
}
82-
}
83-
}
144+
fn call(&self, http_request: Request<IncomingBody>) -> Self::Future {
145+
let state = Arc::new(Mutex::new(self.clone()));
146+
let res = match (http_request.method(), http_request.uri().path()) {
147+
_ => payjoin_handler(http_request, state),
148+
};
84149

85-
fn full<T: Into<Bytes>>(chunk: T) -> BoxBody<Bytes, hyper::Error> {
86-
Full::new(chunk.into()).map_err(|never| match never {}).boxed()
150+
Box::pin(async { res.await })
87151
}
88152
}
153+

0 commit comments

Comments
 (0)