From 703572b46b6cff6b245f14611a0e6278eaaa0cc4 Mon Sep 17 00:00:00 2001 From: DanGould Date: Tue, 30 May 2023 16:01:07 -0400 Subject: [PATCH 01/14] Initialize REElay --- Cargo.lock | 40 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- payjoin-cli/Cargo.toml | 4 ++++ payjoin-cli/src/main.rs | 32 ++++++++++++++++++++++++++++++- payjoin-relay/Cargo.toml | 9 +++++++++ payjoin-relay/README.md | 3 +++ payjoin-relay/src/main.rs | 24 +++++++++++++++++++++++ 7 files changed, 112 insertions(+), 2 deletions(-) create mode 100644 payjoin-relay/Cargo.toml create mode 100644 payjoin-relay/README.md create mode 100644 payjoin-relay/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 239cfa901..24fd0ca80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -475,6 +475,12 @@ dependencies = [ "syn 1.0.107", ] +[[package]] +name = "data-encoding" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" + [[package]] name = "deflate" version = "1.0.0" @@ -1256,6 +1262,15 @@ dependencies = [ "reqwest", "rouille", "serde", + "tungstenite", + "url", +] + +[[package]] +name = "payjoin-relay" +version = "0.0.1" +dependencies = [ + "tungstenite", ] [[package]] @@ -1952,6 +1967,25 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "tungstenite" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" +dependencies = [ + "byteorder", + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand", + "sha1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "twoway" version = "0.1.8" @@ -2026,6 +2060,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 427cff3d1..b22d99542 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["payjoin", "payjoin-cli"] +members = ["payjoin", "payjoin-cli", "payjoin-relay"] diff --git a/payjoin-cli/Cargo.toml b/payjoin-cli/Cargo.toml index 27d2afda8..95639c271 100644 --- a/payjoin-cli/Cargo.toml +++ b/payjoin-cli/Cargo.toml @@ -10,6 +10,7 @@ path = "src/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] +#reelay = ["tungstenite"] native-tls-vendored = ["reqwest/native-tls-vendored"] [dependencies] @@ -25,3 +26,6 @@ payjoin = { path = "../payjoin", features = ["send", "receive"] } reqwest = { version = "0.11.4", features = ["blocking"] } rouille = "3.6.1" serde = { version = "1.0.160", features = ["derive"] } +#tungstenite = { version = "0.19.0", optional = true } +url = "2.3.1" +tungstenite = "0.19.0" \ No newline at end of file diff --git a/payjoin-cli/src/main.rs b/payjoin-cli/src/main.rs index d547235f2..854fb1c7b 100644 --- a/payjoin-cli/src/main.rs +++ b/payjoin-cli/src/main.rs @@ -3,6 +3,8 @@ use clap::{arg, Arg, ArgMatches, Command}; mod app; use app::{App, AppConfig}; +use tungstenite::{connect, Message}; +use url::Url; fn main() -> Result<()> { env_logger::init(); @@ -17,9 +19,29 @@ fn main() -> Result<()> { app.send_payjoin(bip21)?; } Some(("receive", sub_matches)) => { + + let amount = sub_matches.get_one::("AMOUNT").context("Missing AMOUNT argument")?; - app.receive_payjoin(amount)?; + if *sub_matches.get_one::("reelay").context("Missing REELAY argument")? { + println!("REElay"); + let (mut socket, response) = + connect(Url::parse("ws://localhost:3012/socket").unwrap()).expect("Can't connect"); + + println!("Connected to the server"); + println!("Response HTTP code: {}", response.status()); + println!("Response contains the following headers:"); + for (ref header, _value) in response.headers() { + println!("* {}", header); + } + socket.write_message(Message::Text("Hello WebSocket".into())).unwrap(); + loop { + let msg = socket.read_message().expect("Error reading message"); + println!("Received: {}", msg); + } + } else { + app.receive_payjoin(amount)?; + } } _ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!() } @@ -66,6 +88,14 @@ fn cli() -> ArgMatches { .long("endpoint") .short('e') .help("The `pj=` endpoint to receive the payjoin request")) + .arg(Arg::new("reelay") + .long("reelay") + .short('r') + .required(false) + .value_parser(clap::value_parser!(bool)) + .num_args(0..=1) + .default_missing_value("false") + .help("Have a REElay host the payjoin endpoint on our behalf 😡")) .arg(Arg::new("sub_only") .long("sub-only") .short('s') diff --git a/payjoin-relay/Cargo.toml b/payjoin-relay/Cargo.toml new file mode 100644 index 000000000..71efae040 --- /dev/null +++ b/payjoin-relay/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "payjoin-relay" +version = "0.0.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tungstenite = "0.19.0" \ No newline at end of file diff --git a/payjoin-relay/README.md b/payjoin-relay/README.md new file mode 100644 index 000000000..8833d5c09 --- /dev/null +++ b/payjoin-relay/README.md @@ -0,0 +1,3 @@ +# payjoin-relay + +The simplest relay for bip78 payjoin. You have no privacy versus the server! diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs new file mode 100644 index 000000000..1902a3851 --- /dev/null +++ b/payjoin-relay/src/main.rs @@ -0,0 +1,24 @@ +use std::net::TcpListener; +use std::thread::spawn; +use tungstenite::accept; + +fn main() { + let mut buf: String = "".to_string(); + + let server = TcpListener::bind("127.0.0.1:3012").unwrap(); + println!("REElay listening on 127.0.0.1:3012 😡"); + for stream in server.incoming() { + spawn (move || { + let mut websocket = accept(stream.unwrap()).unwrap(); + loop { + let msg = websocket.read_message().unwrap(); + + // We do not want to send back ping/pong messages. + println!("Received: {}", msg); + if msg.is_binary() || msg.is_text() { + websocket.write_message(msg).unwrap(); + } + } + }); + } +} From 1aed4d62bbeb179c8021a6ae73578e79cf22f6ed Mon Sep 17 00:00:00 2001 From: DanGould Date: Tue, 30 May 2023 16:17:59 -0400 Subject: [PATCH 02/14] Use reelay feature conditional compile --- payjoin-cli/Cargo.toml | 7 +++---- payjoin-cli/src/app.rs | 25 +++++++++++++++++++++++++ payjoin-cli/src/main.rs | 35 +++++------------------------------ payjoin-relay/src/main.rs | 3 ++- 4 files changed, 35 insertions(+), 35 deletions(-) diff --git a/payjoin-cli/Cargo.toml b/payjoin-cli/Cargo.toml index 95639c271..dc61f8314 100644 --- a/payjoin-cli/Cargo.toml +++ b/payjoin-cli/Cargo.toml @@ -10,7 +10,7 @@ path = "src/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -#reelay = ["tungstenite"] +reelay = ["tungstenite", "url"] native-tls-vendored = ["reqwest/native-tls-vendored"] [dependencies] @@ -26,6 +26,5 @@ payjoin = { path = "../payjoin", features = ["send", "receive"] } reqwest = { version = "0.11.4", features = ["blocking"] } rouille = "3.6.1" serde = { version = "1.0.160", features = ["derive"] } -#tungstenite = { version = "0.19.0", optional = true } -url = "2.3.1" -tungstenite = "0.19.0" \ No newline at end of file +tungstenite = { version = "0.19.0", optional = true } +url = {version= "2.3.1", optional = true} diff --git a/payjoin-cli/src/app.rs b/payjoin-cli/src/app.rs index d61609a53..fddd7163d 100644 --- a/payjoin-cli/src/app.rs +++ b/payjoin-cli/src/app.rs @@ -120,6 +120,7 @@ impl App { Ok(()) } + #[cfg(not(feature = "reelay"))] pub fn receive_payjoin(self, amount_arg: &str) -> Result<()> { use payjoin::Uri; @@ -463,3 +464,27 @@ impl payjoin::receive::Headers for Headers<'_> { } fn serialize_psbt(psbt: &Psbt) -> String { base64::encode(&psbt.serialize()) } + +#[cfg(feature = "reelay")] +use tungstenite::{connect, Message}; +use url::Url; + +impl App { + pub fn reeceive_payjoin(self, amount_arg: &str) -> Result<()> { + println!("REElay"); + let (mut socket, response) = + connect(Url::parse("ws://localhost:3012/socket").unwrap()).expect("Can't connect"); + + println!("Connected to the server"); + println!("Response HTTP code: {}", response.status()); + println!("Response contains the following headers:"); + for (ref header, _value) in response.headers() { + println!("* {}", header); + } + socket.write_message(Message::Text("Hello WebSocket".into())).unwrap(); + loop { + let msg = socket.read_message().expect("Error reading message"); + println!("Received: {}", msg); + } + } +} diff --git a/payjoin-cli/src/main.rs b/payjoin-cli/src/main.rs index 854fb1c7b..4ba9b5913 100644 --- a/payjoin-cli/src/main.rs +++ b/payjoin-cli/src/main.rs @@ -3,8 +3,6 @@ use clap::{arg, Arg, ArgMatches, Command}; mod app; use app::{App, AppConfig}; -use tungstenite::{connect, Message}; -use url::Url; fn main() -> Result<()> { env_logger::init(); @@ -19,29 +17,14 @@ fn main() -> Result<()> { app.send_payjoin(bip21)?; } Some(("receive", sub_matches)) => { - - let amount = sub_matches.get_one::("AMOUNT").context("Missing AMOUNT argument")?; - if *sub_matches.get_one::("reelay").context("Missing REELAY argument")? { - println!("REElay"); - let (mut socket, response) = - connect(Url::parse("ws://localhost:3012/socket").unwrap()).expect("Can't connect"); - println!("Connected to the server"); - println!("Response HTTP code: {}", response.status()); - println!("Response contains the following headers:"); - for (ref header, _value) in response.headers() { - println!("* {}", header); - } - socket.write_message(Message::Text("Hello WebSocket".into())).unwrap(); - loop { - let msg = socket.read_message().expect("Error reading message"); - println!("Received: {}", msg); - } - } else { - app.receive_payjoin(amount)?; - } + #[cfg(feature = "reelay")] + app.reeceive_payjoin(amount)?; + + #[cfg(not(feature = "reelay"))] + app.receive_payjoin(amount)?; } _ => unreachable!(), // If all subcommands are defined above, anything else is unreachabe!() } @@ -88,14 +71,6 @@ fn cli() -> ArgMatches { .long("endpoint") .short('e') .help("The `pj=` endpoint to receive the payjoin request")) - .arg(Arg::new("reelay") - .long("reelay") - .short('r') - .required(false) - .value_parser(clap::value_parser!(bool)) - .num_args(0..=1) - .default_missing_value("false") - .help("Have a REElay host the payjoin endpoint on our behalf 😡")) .arg(Arg::new("sub_only") .long("sub-only") .short('s') diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 1902a3851..488f4344b 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -1,5 +1,6 @@ use std::net::TcpListener; use std::thread::spawn; + use tungstenite::accept; fn main() { @@ -8,7 +9,7 @@ fn main() { let server = TcpListener::bind("127.0.0.1:3012").unwrap(); println!("REElay listening on 127.0.0.1:3012 😡"); for stream in server.incoming() { - spawn (move || { + spawn(move || { let mut websocket = accept(stream.unwrap()).unwrap(); loop { let msg = websocket.read_message().unwrap(); From 4f182044dbaeb3aa84c426289937e06f1d821525 Mon Sep 17 00:00:00 2001 From: DanGould Date: Wed, 31 May 2023 09:51:47 -0400 Subject: [PATCH 03/14] Serve concurrent HTTP and WS --- Cargo.lock | 244 +++++++++++++++++++++++++++++++++----- payjoin-relay/Cargo.toml | 6 +- payjoin-relay/src/main.rs | 75 +++++++++--- 3 files changed, 280 insertions(+), 45 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 24fd0ca80..838c696a4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -619,47 +619,88 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed" +checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" dependencies = [ "futures-core", + "futures-sink", ] [[package]] name = "futures-core" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" +checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" + +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] [[package]] name = "futures-io" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb" +checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" + +[[package]] +name = "futures-macro" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", +] [[package]] name = "futures-sink" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" +checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" [[package]] name = "futures-task" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" +checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" [[package]] name = "futures-util" -version = "0.3.25" +version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", "futures-io", + "futures-macro", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -699,9 +740,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.15" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" dependencies = [ "bytes", "fnv", @@ -797,9 +838,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.23" +version = "0.14.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034711faac9d2166cb1baf1a2fb0b60b1f277f8492fd72176c17f3515e1abd3c" +checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" dependencies = [ "bytes", "futures-channel", @@ -994,6 +1035,16 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -1227,6 +1278,29 @@ version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys 0.45.0", +] + [[package]] name = "pathdiff" version = "0.2.1" @@ -1270,6 +1344,10 @@ dependencies = [ name = "payjoin-relay" version = "0.0.1" dependencies = [ + "futures", + "hyper", + "tokio", + "tokio-tungstenite", "tungstenite", ] @@ -1588,6 +1666,12 @@ dependencies = [ "windows-sys 0.36.1", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "scratch" version = "1.0.3" @@ -1713,6 +1797,15 @@ dependencies = [ "digest", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "slab" version = "0.4.7" @@ -1722,11 +1815,17 @@ dependencies = [ "autocfg", ] +[[package]] +name = "smallvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" + [[package]] name = "socket2" -version = "0.4.7" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" dependencies = [ "libc", "winapi", @@ -1887,19 +1986,32 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.23.0" +version = "1.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab6d665857cc6ca78d6e80303a02cea7a7851e85dfbd77cbdc09bd129f1ef46" +checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" dependencies = [ "autocfg", "bytes", "libc", - "memchr", "mio", "num_cpus", + "parking_lot", "pin-project-lite", + "signal-hook-registry", "socket2", - "windows-sys 0.42.0", + "tokio-macros", + "windows-sys 0.48.0", +] + +[[package]] +name = "tokio-macros" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.15", ] [[package]] @@ -1912,6 +2024,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-tungstenite" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.4" @@ -2256,12 +2380,12 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] @@ -2271,7 +2395,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -2280,21 +2413,42 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" dependencies = [ - "windows_aarch64_gnullvm", + "windows_aarch64_gnullvm 0.42.1", "windows_aarch64_msvc 0.42.1", "windows_i686_gnu 0.42.1", "windows_i686_msvc 0.42.1", "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm", + "windows_x86_64_gnullvm 0.42.1", "windows_x86_64_msvc 0.42.1", ] +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.36.1" @@ -2307,6 +2461,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.36.1" @@ -2319,6 +2479,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.36.1" @@ -2331,6 +2497,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.36.1" @@ -2343,12 +2515,24 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.36.1" @@ -2361,6 +2545,12 @@ version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" + [[package]] name = "winreg" version = "0.10.1" diff --git a/payjoin-relay/Cargo.toml b/payjoin-relay/Cargo.toml index 71efae040..39a5ed66a 100644 --- a/payjoin-relay/Cargo.toml +++ b/payjoin-relay/Cargo.toml @@ -6,4 +6,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tungstenite = "0.19.0" \ No newline at end of file +futures = "0.3.28" +hyper = { version = "0.14.26", features = ["server"] } +tokio = { version = "1.28.2", features = ["full"] } +tungstenite = "0.19.0" +tokio-tungstenite = "*" \ No newline at end of file diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 488f4344b..94e6b15ce 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -1,25 +1,66 @@ +use std::fmt; use std::net::TcpListener; -use std::thread::spawn; - use tungstenite::accept; +use tungstenite::Message; +use hyper::{Body, Request, Response, Server}; +use hyper::service::{make_service_fn, service_fn}; +use hyper::StatusCode; +use tokio::sync::mpsc; -fn main() { - let mut buf: String = "".to_string(); - +#[tokio::main] +async fn main() { let server = TcpListener::bind("127.0.0.1:3012").unwrap(); - println!("REElay listening on 127.0.0.1:3012 😡"); - for stream in server.incoming() { - spawn(move || { + println!("REElay listening on ws://127.0.0.1:3012 😡"); + let (tx, mut rx) = mpsc::channel(1); + + tokio::spawn(async move { + for stream in server.incoming() { + println!("New ws connection!"); let mut websocket = accept(stream.unwrap()).unwrap(); - loop { - let msg = websocket.read_message().unwrap(); + let msg = websocket.read_message().unwrap(); + println!("Received: {}", msg); + let res = rx.recv().await.unwrap(); + websocket.write_message(res).unwrap(); + } + }); + + // run HTTP server. On Post PJ, relay to websocket + let make_svc = make_service_fn(move |_| { + let tx = tx.clone(); + async move { + let handler = move |req| handle_http_req(tx.clone(), req); + Ok::<_, hyper::Error>(service_fn(handler)) + } + }); + + let server = Server::bind(&([127, 0, 0, 1], 8080).into()).serve(make_svc); + println!("REElay configured to listen on http://127.0.0.1:8080 😡"); + server.await.unwrap(); +} - // We do not want to send back ping/pong messages. - println!("Received: {}", msg); - if msg.is_binary() || msg.is_text() { - websocket.write_message(msg).unwrap(); - } - } - }); +async fn handle_http_req( + tx: mpsc::Sender, + req: Request, +) -> Result, hyper::Error> { + match (req.method().clone(), req.uri().path()) { + (hyper::Method::POST, "/") => { + println!("POST / received"); + let entire_body = hyper::body::to_bytes(req.into_body()).await.unwrap(); + tx.send(Message::Text(String::from_utf8(entire_body.to_vec()).unwrap())).await.unwrap(); + Ok::, hyper::Error>(Response::new(Body::from("Posted"))) + }, + _ => { + let mut not_found = Response::default(); + *not_found.status_mut() = StatusCode::NOT_FOUND; + Ok(not_found) + } } } + +struct ServerError {} + +impl fmt::Display for ServerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Server error") + } +} \ No newline at end of file From 54f60d6d67d88232a939446a0b99de37af49601e Mon Sep 17 00:00:00 2001 From: DanGould Date: Wed, 31 May 2023 10:13:04 -0400 Subject: [PATCH 04/14] Ignore seen_inputs .vscode/ .DS_Store --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1b482004d..0279124fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ /target -*config.toml \ No newline at end of file +*config.toml +*seen_inputs.json +.vscode/ +.DS_Store \ No newline at end of file From 4706f48503f11ba83f40362315550554b035a445 Mon Sep 17 00:00:00 2001 From: DanGould Date: Wed, 31 May 2023 17:18:44 -0400 Subject: [PATCH 05/14] Relay HTTP data to WS & back --- payjoin-relay/src/main.rs | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 94e6b15ce..8c1d2424d 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -5,22 +5,26 @@ use tungstenite::Message; use hyper::{Body, Request, Response, Server}; use hyper::service::{make_service_fn, service_fn}; use hyper::StatusCode; -use tokio::sync::mpsc; +use tokio::sync::{mpsc, oneshot}; #[tokio::main] async fn main() { let server = TcpListener::bind("127.0.0.1:3012").unwrap(); println!("REElay listening on ws://127.0.0.1:3012 😡"); - let (tx, mut rx) = mpsc::channel(1); + let (tx, mut rx) = mpsc::channel::<(Message, oneshot::Sender)>(1); tokio::spawn(async move { for stream in server.incoming() { println!("New ws connection!"); let mut websocket = accept(stream.unwrap()).unwrap(); let msg = websocket.read_message().unwrap(); - println!("Received: {}", msg); - let res = rx.recv().await.unwrap(); - websocket.write_message(res).unwrap(); + println!("Received: {}, awaiting Original PSBT", msg); + let (post, res_tx): (Message, _) = rx.recv().await.unwrap(); + println!("Received Original PSBT, relaying to websocket"); + websocket.write_message(post).unwrap(); + // TODO await ws client transform Original PSBT into Payjoin PSBT + let payjoin_psbt = ""; + res_tx.send(payjoin_psbt.to_string()).unwrap(); } }); @@ -39,15 +43,18 @@ async fn main() { } async fn handle_http_req( - tx: mpsc::Sender, + tx: mpsc::Sender<(Message, oneshot::Sender)>, req: Request, ) -> Result, hyper::Error> { match (req.method().clone(), req.uri().path()) { (hyper::Method::POST, "/") => { - println!("POST / received"); + println!("POST / received"); let entire_body = hyper::body::to_bytes(req.into_body()).await.unwrap(); - tx.send(Message::Text(String::from_utf8(entire_body.to_vec()).unwrap())).await.unwrap(); - Ok::, hyper::Error>(Response::new(Body::from("Posted"))) + let (res_tx, res_rx) = oneshot::channel(); + tx.send((Message::Text(String::from_utf8(entire_body.to_vec()).unwrap()), res_tx)).await.unwrap(); + // await payjoin PSBT + let payjoin_psbt = res_rx.await.unwrap(); + Ok::, hyper::Error>(Response::new(Body::from(payjoin_psbt))) }, _ => { let mut not_found = Response::default(); From ad65c71917843fc044f0579344d35ebd9bfb0a32 Mon Sep 17 00:00:00 2001 From: DanGould Date: Thu, 1 Jun 2023 11:25:22 -0400 Subject: [PATCH 06/14] Abstract make_pj_uri_string --- payjoin-cli/src/app.rs | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/payjoin-cli/src/app.rs b/payjoin-cli/src/app.rs index fddd7163d..8b1f618f4 100644 --- a/payjoin-cli/src/app.rs +++ b/payjoin-cli/src/app.rs @@ -122,30 +122,35 @@ impl App { #[cfg(not(feature = "reelay"))] pub fn receive_payjoin(self, amount_arg: &str) -> Result<()> { + let amount = Amount::from_sat(amount_arg.parse()?); + let pj_uri_string = self.make_pj_uri_string(amount)?; + + println!( + "Listening at {}. Configured to accept payjoin at BIP 21 Payjoin Uri:", + self.config.pj_host + ); + println!("{}", pj_uri_string); + + rouille::start_server(self.config.pj_host.clone(), move |req| self.handle_web_request(req)); + } + + fn make_pj_uri_string(&self, amount: Amount) -> Result { use payjoin::Uri; - let pj_receiver_address = self.bitcoind.get_new_address(None, None)?.assume_checked(); - let amount = Amount::from_sat(amount_arg.parse()?); + let address = self.bitcoind.get_new_address(None, None)?.assume_checked(); let pj_uri_string = format!( "{}?amount={}&pj={}", - pj_receiver_address.to_qr_uri(), + address.to_qr_uri(), amount.to_btc(), self.config.pj_endpoint ); let pj_uri = Uri::from_str(&pj_uri_string) - .map_err(|e| anyhow!("Constructed a bad URI string from args: {}", e))?; + .map_err(|e| anyhow!("Constructed a bad URI string from args: {}", e))?; let _pj_uri = pj_uri .assume_checked() .check_pj_supported() .map_err(|e| anyhow!("Constructed URI does not support payjoin: {}", e))?; - - println!( - "Listening at {}. Configured to accept payjoin at BIP 21 Payjoin Uri:", - self.config.pj_host - ); - println!("{}", pj_uri_string); - - rouille::start_server(self.config.pj_host.clone(), move |req| self.handle_web_request(req)); + Ok(pj_uri_string) } fn handle_web_request(&self, req: &Request) -> Response { @@ -467,10 +472,12 @@ fn serialize_psbt(psbt: &Psbt) -> String { base64::encode(&psbt.serialize()) } #[cfg(feature = "reelay")] use tungstenite::{connect, Message}; -use url::Url; +#[cfg(feature = "reelay")] +use url::Url; +#[cfg(feature = "reelay")] impl App { - pub fn reeceive_payjoin(self, amount_arg: &str) -> Result<()> { + pub fn reeceive_payjoin(self, amount: Amount) -> Result<()> { println!("REElay"); let (mut socket, response) = connect(Url::parse("ws://localhost:3012/socket").unwrap()).expect("Can't connect"); From 3ed27534887ce5edf97a32b2d19c8eab26a329ae Mon Sep 17 00:00:00 2001 From: DanGould Date: Fri, 2 Jun 2023 16:07:56 -0400 Subject: [PATCH 07/14] REElay bip78 payjoin with ws receiver --- Cargo.lock | 86 +++++++++++++++++++----- payjoin-cli/Cargo.toml | 6 +- payjoin-cli/src/app.rs | 130 ++++++++++++++++++++++++++++--------- payjoin-relay/Cargo.toml | 9 ++- payjoin-relay/src/main.rs | 68 +++++++++++-------- payjoin/Cargo.toml | 7 ++ payjoin/src/lib.rs | 3 + payjoin/src/receive/mod.rs | 2 + payjoin/src/relay.rs | 33 ++++++++++ 9 files changed, 265 insertions(+), 79 deletions(-) create mode 100644 payjoin/src/relay.rs diff --git a/Cargo.lock b/Cargo.lock index 838c696a4..5b45722d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -361,6 +361,15 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "cookie" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" +dependencies = [ + "time 0.1.45", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -766,6 +775,31 @@ dependencies = [ "ahash", ] +[[package]] +name = "headers" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" +dependencies = [ + "base64", + "bitflags", + "bytes", + "headers-core", + "http", + "httpdate", + "mime", + "sha1", +] + +[[package]] +name = "headers-core" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" +dependencies = [ + "http", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -873,6 +907,22 @@ dependencies = [ "tokio-native-tls", ] +[[package]] +name = "hyper_serde" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4193ebe2b9ecb7d04dd1a777c25f3294e10310fe5583e1006a3f47779d6c6a01" +dependencies = [ + "cookie", + "headers", + "http", + "hyper", + "mime", + "serde", + "serde_bytes", + "time 0.1.45", +] + [[package]] name = "iana-time-zone" version = "0.1.53" @@ -1315,8 +1365,12 @@ dependencies = [ "bitcoin", "bitcoind", "env_logger", + "hyper", + "hyper_serde", "log", "rand", + "serde", + "serde_json", "url", ] @@ -1331,11 +1385,13 @@ dependencies = [ "clap", "config", "env_logger", + "hyper_serde", "log", "payjoin", "reqwest", "rouille", "serde", + "serde_json", "tungstenite", "url", ] @@ -1346,8 +1402,11 @@ version = "0.0.1" dependencies = [ "futures", "hyper", + "hyper_serde", + "payjoin", + "serde", + "serde_json", "tokio", - "tokio-tungstenite", "tungstenite", ] @@ -1741,6 +1800,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_bytes" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" version = "1.0.160" @@ -1754,9 +1822,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.95" +version = "1.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" +checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" dependencies = [ "itoa", "ryu", @@ -2024,18 +2092,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-tungstenite" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec509ac96e9a0c43427c74f003127d953a265737636129424288d27cb5c4b12c" -dependencies = [ - "futures-util", - "log", - "tokio", - "tungstenite", -] - [[package]] name = "tokio-util" version = "0.7.4" diff --git a/payjoin-cli/Cargo.toml b/payjoin-cli/Cargo.toml index dc61f8314..f85cbf67b 100644 --- a/payjoin-cli/Cargo.toml +++ b/payjoin-cli/Cargo.toml @@ -10,7 +10,7 @@ path = "src/main.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -reelay = ["tungstenite", "url"] +reelay = ["payjoin/relay", "tungstenite", "url"] native-tls-vendored = ["reqwest/native-tls-vendored"] [dependencies] @@ -21,10 +21,12 @@ bitcoincore-rpc = "0.17.0" clap = "4.1.4" config = "0.13.3" env_logger = "0.9.0" +hyper_serde = "0.13.0" log = "0.4.7" payjoin = { path = "../payjoin", features = ["send", "receive"] } reqwest = { version = "0.11.4", features = ["blocking"] } rouille = "3.6.1" serde = { version = "1.0.160", features = ["derive"] } +serde_json = { version = "1.0.96" } tungstenite = { version = "0.19.0", optional = true } -url = {version= "2.3.1", optional = true} +url = { version= "2.3.1", optional = true } \ No newline at end of file diff --git a/payjoin-cli/src/app.rs b/payjoin-cli/src/app.rs index 8b1f618f4..10a9d0f8a 100644 --- a/payjoin-cli/src/app.rs +++ b/payjoin-cli/src/app.rs @@ -1,3 +1,4 @@ +use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::convert::TryFrom; use std::str::FromStr; @@ -87,6 +88,7 @@ impl App { let (req, ctx) = link .create_pj_request(psbt, pj_params) .with_context(|| "Failed to create payjoin request")?; + log::debug!("Sending payjoin request body: {:#?}", req.body); let client = reqwest::blocking::Client::builder() .danger_accept_invalid_certs(self.config.danger_accept_invalid_certs) .build() @@ -168,19 +170,26 @@ impl App { }) .unwrap_or_else(|err_resp| err_resp) } - ("POST", _) => self - .handle_payjoin_post(req) - .map_err(|e| match e { - Error::BadRequest(e) => { - log::error!("Error handling request: {}", e); - Response::text(e.to_string()).with_status_code(400) - } - e => { - log::error!("Error handling request: {}", e); - Response::text(e.to_string()).with_status_code(500) - } - }) - .unwrap_or_else(|err_resp| err_resp), + ("POST", _) => { + let headers = RouilleHeaders(req.headers()); + let body = req.data().context("Failed to read request body").map_err(|e| { + log::warn!("Failed to read request body: {}", e); + Error::Server(e.into()) + }).unwrap(); // return Response::text(e.to_string()).with_status_code(500) + self + .handle_payjoin_post(body, req.raw_query_string(), headers) + .map_err(|e| match e { + Error::BadRequest(e) => { + log::error!("Error handling request: {}", e); + Response::text(e.to_string()).with_status_code(400) + } + e => { + log::error!("Error handling request: {}", e); + Response::text(e.to_string()).with_status_code(500) + } + }) + .unwrap_or_else(|err_resp| err_resp) + } _ => Response::empty_404(), } } @@ -210,17 +219,10 @@ impl App { Ok(Response::text(uri_string)) } - fn handle_payjoin_post(&self, req: &Request) -> Result { - let headers = Headers(req.headers()); - let proposal = payjoin::receive::UncheckedProposal::from_request( - req.data().context("Failed to read request body").map_err(|e| { - log::warn!("Failed to read request body: {}", e); - Error::Server(e.into()) - })?, - req.raw_query_string(), - headers, - )?; - + fn handle_payjoin_post(&self, body: impl std::io::Read, query: &str, headers: impl payjoin::receive::Headers) -> Result { + log::debug!("handle_payjoin_post"); + let proposal = payjoin::receive::UncheckedProposal::from_request(body, query, headers)?; + // in a payment processor where the sender could go offline, this is where you schedule to broadcast the original_tx let _to_broadcast_in_failure_case = proposal.get_transaction_to_schedule_broadcast(); @@ -460,8 +462,8 @@ fn try_contributing_inputs( Ok(()) } -struct Headers<'a>(rouille::HeadersIter<'a>); -impl payjoin::receive::Headers for Headers<'_> { +struct RouilleHeaders<'a>(rouille::HeadersIter<'a>); +impl payjoin::receive::Headers for RouilleHeaders<'_> { fn get_header(&self, key: &str) -> Option<&str> { let mut copy = self.0.clone(); // lol copy.find(|(k, _)| k.eq_ignore_ascii_case(key)).map(|(_, v)| v) @@ -477,10 +479,25 @@ use tungstenite::{connect, Message}; use url::Url; #[cfg(feature = "reelay")] impl App { - pub fn reeceive_payjoin(self, amount: Amount) -> Result<()> { + pub fn reeceive_payjoin(self, amount_arg: &str) -> Result<()> { + use std::io::Read; + use payjoin::relay; + + let amount = Amount::from_sat(amount_arg.parse()?); + let pj_uri_string = self.make_pj_uri_string(amount)?; + + println!("{}", pj_uri_string); + let ws_uri = "ws://localhost:3012/socket"; + println!( + "Listening via relay at {}. Configured to accept payjoin at BIP 21 Payjoin Uri:", + self.config.pj_host + ); + println!("{}", pj_uri_string); + + // listen for incoming payjoin requests at ws println!("REElay"); let (mut socket, response) = - connect(Url::parse("ws://localhost:3012/socket").unwrap()).expect("Can't connect"); + connect(Url::parse(ws_uri).unwrap()).expect("Can't connect"); println!("Connected to the server"); println!("Response HTTP code: {}", response.status()); @@ -488,10 +505,59 @@ impl App { for (ref header, _value) in response.headers() { println!("* {}", header); } + socket.write_message(Message::Text("Hello WebSocket".into())).unwrap(); - loop { - let msg = socket.read_message().expect("Error reading message"); - println!("Received: {}", msg); - } + println!("Waiting for messages..."); + let msg = socket.read_message().expect("Error reading message"); + println!("Received Request, deserializing: {}", msg); + // for a production protocol bhttp would make more sense than json + let req: relay::Request = serde_json::from_str(&msg.to_text().unwrap()).unwrap(); + println!("Deserialized request: {:?}", req); + //let body = req.body + println!("desrialized headers: {:?}", req.headers); + let headers = HyperHeaders(req.headers); + let body = std::io::Cursor::new(req.body); + println!("deserialized body: {:?}", body); + println!("Handling relayed payjoin POST request"); + // do receive + let res = self.handle_payjoin_post(body, &req.query, headers) + .map_err(|e| match e { + Error::BadRequest(e) => { + log::error!("Error handling request: {}", e); + Response::text(e.to_string()).with_status_code(400) + } + e => { + log::error!("Error handling request: {}", e); + Response::text(e.to_string()).with_status_code(500) + } + }) + .unwrap_or_else(|err_resp| err_resp); + println!("Serializing response"); + let (mut reader, _usize) = res.data.into_reader_and_size(); + let mut body = Vec::::new(); + reader.read_to_end(&mut body).unwrap(); + println!("read body into buffer"); + let headers = relay::VecHeaders::new(res.headers); + let res = relay::Response { + body, + headers, + status_code: res.status_code, + }; + let res = serde_json::to_string(&res).unwrap(); + println!("serialized res: {:#?}", res); + socket.write_message(Message::Text(res)).unwrap(); + socket.close(None).unwrap(); + + Ok(()) } } + +#[cfg(feature = "reelay")] +#[derive(Debug)] +struct HyperHeaders(reqwest::header::HeaderMap); +#[cfg(feature = "reelay")] +impl payjoin::receive::Headers for HyperHeaders { + fn get_header(&self, key: &str) -> Option<&str> { + self.0.get(key).and_then(|v| v.to_str().ok()) + } +} \ No newline at end of file diff --git a/payjoin-relay/Cargo.toml b/payjoin-relay/Cargo.toml index 39a5ed66a..594110e8a 100644 --- a/payjoin-relay/Cargo.toml +++ b/payjoin-relay/Cargo.toml @@ -7,7 +7,10 @@ edition = "2021" [dependencies] futures = "0.3.28" -hyper = { version = "0.14.26", features = ["server"] } +hyper = { version = "0.14.26", features = ["full"] } +hyper_serde = "0.13.0" +payjoin = { path = "../payjoin", features = ["relay"] } +serde = { version = "1.0.160", features = ["derive"] } +serde_json = "1.0.96" tokio = { version = "1.28.2", features = ["full"] } -tungstenite = "0.19.0" -tokio-tungstenite = "*" \ No newline at end of file +tungstenite = "0.19.0" \ No newline at end of file diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 8c1d2424d..70d25f4b9 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -1,17 +1,18 @@ -use std::fmt; use std::net::TcpListener; use tungstenite::accept; -use tungstenite::Message; -use hyper::{Body, Request, Response, Server}; +use hyper::Body; use hyper::service::{make_service_fn, service_fn}; use hyper::StatusCode; use tokio::sync::{mpsc, oneshot}; +use tungstenite::Message; + +use payjoin::relay; #[tokio::main] async fn main() { let server = TcpListener::bind("127.0.0.1:3012").unwrap(); println!("REElay listening on ws://127.0.0.1:3012 😡"); - let (tx, mut rx) = mpsc::channel::<(Message, oneshot::Sender)>(1); + let (tx, mut rx) = mpsc::channel::<(relay::Request, oneshot::Sender)>(1); tokio::spawn(async move { for stream in server.incoming() { @@ -19,12 +20,23 @@ async fn main() { let mut websocket = accept(stream.unwrap()).unwrap(); let msg = websocket.read_message().unwrap(); println!("Received: {}, awaiting Original PSBT", msg); - let (post, res_tx): (Message, _) = rx.recv().await.unwrap(); - println!("Received Original PSBT, relaying to websocket"); + let (relay_req, res_tx) = rx.recv().await.unwrap(); + // relay Original PSBT request to receiver via websocket + let serialized_req = serde_json::to_string(&relay_req).unwrap(); + let post = Message::Text(serialized_req.to_string()); + println!("Received Original PSBT, relaying to receiver via websocket"); websocket.write_message(post).unwrap(); + println!("Awaiting Payjoin PSBT from receiver via websocket"); // does this need to be async? break because block? // TODO await ws client transform Original PSBT into Payjoin PSBT - let payjoin_psbt = ""; - res_tx.send(payjoin_psbt.to_string()).unwrap(); + let msg = websocket.read_message().unwrap(); + let serialized_res = msg.into_text().unwrap(); + let res = serde_json::from_str::(&serialized_res).unwrap(); + println!("Received Payjoin PSBT res {:#?}, relaying to sender via http", serialized_res); + // delay 200ms + tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + let sent = res_tx.send(res); + println!("sent to http server via res_tx: {:?}", sent); + break; } }); @@ -37,37 +49,39 @@ async fn main() { } }); - let server = Server::bind(&([127, 0, 0, 1], 8080).into()).serve(make_svc); - println!("REElay configured to listen on http://127.0.0.1:8080 😡"); + let server = hyper::Server::bind(&([127, 0, 0, 1], 3000).into()).serve(make_svc); + println!("REElay configured to listen on http://127.0.0.1:3000 😡"); server.await.unwrap(); } async fn handle_http_req( - tx: mpsc::Sender<(Message, oneshot::Sender)>, - req: Request, -) -> Result, hyper::Error> { + tx: mpsc::Sender<(relay::Request, oneshot::Sender>)>, + req: hyper::Request, +) -> Result, hyper::Error> { + match (req.method().clone(), req.uri().path()) { (hyper::Method::POST, "/") => { println!("POST / received"); - let entire_body = hyper::body::to_bytes(req.into_body()).await.unwrap(); + let header = req.headers().clone(); + let query = req.uri().query().unwrap_or("").to_string(); + let body = hyper::body::to_bytes(req.into_body()).await?.to_vec(); + println!("POST / body: {:?}", body); + let relay_req = relay::Request { headers: header, query, body }; let (res_tx, res_rx) = oneshot::channel(); - tx.send((Message::Text(String::from_utf8(entire_body.to_vec()).unwrap()), res_tx)).await.unwrap(); - // await payjoin PSBT - let payjoin_psbt = res_rx.await.unwrap(); - Ok::, hyper::Error>(Response::new(Body::from(payjoin_psbt))) + tx.send((relay_req, res_tx)).await.unwrap(); + println!("Relayed req to ws channel from HTTP, awaiting Response"); + let res = res_rx.await.unwrap(); // TODO THIS NEVER GETS CALLED??? + println!("POST / response received {:?}", res); + let res = hyper::Response::builder() + .status(StatusCode::from_u16(res.status_code).unwrap()) + .body(Body::from(res.body)) + .unwrap(); + Ok::, hyper::Error>(res) }, _ => { - let mut not_found = Response::default(); + let mut not_found = hyper::Response::default(); *not_found.status_mut() = StatusCode::NOT_FOUND; Ok(not_found) } } } - -struct ServerError {} - -impl fmt::Display for ServerError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "Server error") - } -} \ No newline at end of file diff --git a/payjoin/Cargo.toml b/payjoin/Cargo.toml index 7592ab4a9..e5536228f 100644 --- a/payjoin/Cargo.toml +++ b/payjoin/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" [features] send = [] receive = ["rand"] +relay = ["receive", "hyper/server", "hyper_serde", "serde/derive", "serde_json"] [dependencies] bitcoin = { version = "0.30.0", features = ["base64"] } @@ -23,6 +24,12 @@ log = { version = "0.4.14"} rand = { version = "0.8.4", optional = true } url = "2.2.2" +# relay dependencies +hyper = { version = "0.14.26", features = ["server"], optional = true } +hyper_serde = { version = "0.13.0", optional = true } +serde = { version = "1.0.160", features = ["derive"], optional = true } +serde_json = { version = "1.0.96", optional = true } + [dev-dependencies] env_logger = "0.9.0" bitcoind = { version = "0.31.1", features = ["0_21_2"] } diff --git a/payjoin/src/lib.rs b/payjoin/src/lib.rs index fc787d457..9b676bc31 100644 --- a/payjoin/src/lib.rs +++ b/payjoin/src/lib.rs @@ -24,6 +24,9 @@ pub mod receive; #[cfg(feature = "receive")] pub use crate::receive::Error; +#[cfg(feature = "relay")] +pub mod relay; + #[cfg(feature = "send")] pub mod send; diff --git a/payjoin/src/receive/mod.rs b/payjoin/src/receive/mod.rs index 0f8a0748d..798957d02 100644 --- a/payjoin/src/receive/mod.rs +++ b/payjoin/src/receive/mod.rs @@ -332,9 +332,11 @@ impl UncheckedProposal { query: &str, headers: impl Headers, ) -> Result { + log::debug!("from_request"); let content_type = headers .get_header("content-type") .ok_or(InternalRequestError::MissingHeader("Content-Type"))?; + log::debug!("Content-Type: {}", content_type); if !content_type.starts_with("text/plain") { return Err(InternalRequestError::InvalidContentType(content_type.to_owned()).into()); } diff --git a/payjoin/src/relay.rs b/payjoin/src/relay.rs new file mode 100644 index 000000000..2c4efca14 --- /dev/null +++ b/payjoin/src/relay.rs @@ -0,0 +1,33 @@ + +use std::borrow::Cow; + +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Request { + pub body: Vec, + pub query: String, + #[serde(deserialize_with = "hyper_serde::deserialize", serialize_with = "hyper_serde::serialize")] + pub headers: hyper::HeaderMap, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Response<'a> { + pub body: Vec, + pub headers: VecHeaders<'a>, + pub status_code: u16, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct VecHeaders<'a>(Vec<(Cow<'a, str>, Cow<'a, str>)>); + +impl<'a> VecHeaders<'a> { + pub fn new(headers: Vec<(Cow<'a, str>, Cow<'a, str>)>) -> Self { Self(headers) } +} + +impl crate::receive::Headers for VecHeaders<'_> { + fn get_header(&self, key: &str) -> Option<&str> { + log::debug!("get_header({})", key); + self.0.iter().find(|(k, _)| k.eq_ignore_ascii_case(key)).map(|(_, v)| v.as_ref()) + } +} \ No newline at end of file From f4641da6d75fe02a191feeb7db7f718b1d581c12 Mon Sep 17 00:00:00 2001 From: DanGould Date: Wed, 7 Jun 2023 14:24:16 -0400 Subject: [PATCH 08/14] Specify pjos=0 for REElay --- payjoin-cli/src/app.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/payjoin-cli/src/app.rs b/payjoin-cli/src/app.rs index 10a9d0f8a..8305c41b7 100644 --- a/payjoin-cli/src/app.rs +++ b/payjoin-cli/src/app.rs @@ -484,7 +484,7 @@ impl App { use payjoin::relay; let amount = Amount::from_sat(amount_arg.parse()?); - let pj_uri_string = self.make_pj_uri_string(amount)?; + let pj_uri_string = self.make_pj_uri_string(amount)? + "&pjos=0"; println!("{}", pj_uri_string); let ws_uri = "ws://localhost:3012/socket"; From 10aa3209b4951a4e5c565a48a484826e21e7a8fa Mon Sep 17 00:00:00 2001 From: DanGould Date: Fri, 4 Aug 2023 22:49:37 -0400 Subject: [PATCH 09/14] Persist payload outside connection threads --- payjoin-relay/src/main.rs | 91 +++++++++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 70d25f4b9..b7321db8b 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -1,9 +1,10 @@ use std::net::TcpListener; +use std::sync::Arc; use tungstenite::accept; use hyper::Body; use hyper::service::{make_service_fn, service_fn}; use hyper::StatusCode; -use tokio::sync::{mpsc, oneshot}; +use tokio::sync::{mpsc, oneshot, Mutex}; use tungstenite::Message; use payjoin::relay; @@ -12,39 +13,43 @@ use payjoin::relay; async fn main() { let server = TcpListener::bind("127.0.0.1:3012").unwrap(); println!("REElay listening on ws://127.0.0.1:3012 😡"); - let (tx, mut rx) = mpsc::channel::<(relay::Request, oneshot::Sender)>(1); - + let req_buffer = Buffer::new(); + let res_buffer = Buffer::new(); + + let ws_req_buffer = req_buffer.clone(); + let ws_res_buffer = res_buffer.clone(); tokio::spawn(async move { + // run websocket server. On connection, await Original PSBT, relay to http server for stream in server.incoming() { println!("New ws connection!"); let mut websocket = accept(stream.unwrap()).unwrap(); let msg = websocket.read_message().unwrap(); println!("Received: {}, awaiting Original PSBT", msg); - let (relay_req, res_tx) = rx.recv().await.unwrap(); + + let buffered_req = ws_req_buffer.clone().pop().await; // relay Original PSBT request to receiver via websocket - let serialized_req = serde_json::to_string(&relay_req).unwrap(); - let post = Message::Text(serialized_req.to_string()); + let post = Message::Text(buffered_req.to_string()); println!("Received Original PSBT, relaying to receiver via websocket"); websocket.write_message(post).unwrap(); + println!("Awaiting Payjoin PSBT from receiver via websocket"); // does this need to be async? break because block? // TODO await ws client transform Original PSBT into Payjoin PSBT let msg = websocket.read_message().unwrap(); let serialized_res = msg.into_text().unwrap(); - let res = serde_json::from_str::(&serialized_res).unwrap(); println!("Received Payjoin PSBT res {:#?}, relaying to sender via http", serialized_res); - // delay 200ms - tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; - let sent = res_tx.send(res); - println!("sent to http server via res_tx: {:?}", sent); + + ws_res_buffer.push(serialized_res).await; + println!("sent to http server via push"); break; } }); // run HTTP server. On Post PJ, relay to websocket let make_svc = make_service_fn(move |_| { - let tx = tx.clone(); + let req_buffer = req_buffer.clone(); + let res_buffer = res_buffer.clone(); async move { - let handler = move |req| handle_http_req(tx.clone(), req); + let handler = move |req| handle_http_req(req_buffer.clone(), res_buffer.clone(), req); Ok::<_, hyper::Error>(service_fn(handler)) } }); @@ -55,7 +60,8 @@ async fn main() { } async fn handle_http_req( - tx: mpsc::Sender<(relay::Request, oneshot::Sender>)>, + req_buffer: Buffer, + res_buffer: Buffer, req: hyper::Request, ) -> Result, hyper::Error> { @@ -67,10 +73,12 @@ async fn handle_http_req( let body = hyper::body::to_bytes(req.into_body()).await?.to_vec(); println!("POST / body: {:?}", body); let relay_req = relay::Request { headers: header, query, body }; - let (res_tx, res_rx) = oneshot::channel(); - tx.send((relay_req, res_tx)).await.unwrap(); + let serialized_req = serde_json::to_string(&relay_req).unwrap(); + req_buffer.push(serialized_req).await; println!("Relayed req to ws channel from HTTP, awaiting Response"); - let res = res_rx.await.unwrap(); // TODO THIS NEVER GETS CALLED??? + + let serialized_res = res_buffer.pop().await; + let res = serde_json::from_str::(&serialized_res).unwrap(); println!("POST / response received {:?}", res); let res = hyper::Response::builder() .status(StatusCode::from_u16(res.status_code).unwrap()) @@ -85,3 +93,52 @@ async fn handle_http_req( } } } + +struct Buffer { + buffer: Arc>, + sender: mpsc::Sender<()>, + receiver: Arc>>, +} + +/// Clone here makes a copy of the Arc pointer, not the underlying data +/// All clones point to the same internal data +impl Clone for Buffer { + fn clone(&self) -> Self { + Buffer { + buffer: Arc::clone(&self.buffer), + sender: self.sender.clone(), + receiver: Arc::clone(&self.receiver), + } + } +} + +impl Buffer { + fn new() -> Self { + let (sender, receiver) = mpsc::channel(1); + Buffer { + buffer: Arc::new(Mutex::new(String::new())), + sender, + receiver: Arc::new(Mutex::new(receiver)), + } + } + + async fn push(&self, request: String) { + let mut buffer: tokio::sync::MutexGuard<'_, String> = self.buffer.lock().await; + *buffer = request; + let _ = self.sender.send(()).await; // signal that a new request has been added + } + + async fn pop(&self) -> String { + let mut buffer = self.buffer.lock().await; + let mut contents = buffer.clone(); + if contents.is_empty() { + drop(buffer); + // wait for a signal that a new request has been added + self.receiver.lock().await.recv().await; + buffer = self.buffer.lock().await; + contents = buffer.clone(); + } + *buffer = String::new(); + contents + } +} From e62d56e7466ef71f1fdc7fd6bc48eb4b175e464a Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 5 Aug 2023 10:23:46 -0400 Subject: [PATCH 10/14] Act on websocket condition --- payjoin-cli/src/app.rs | 2 +- payjoin-relay/src/main.rs | 32 +++++++++++++++++--------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/payjoin-cli/src/app.rs b/payjoin-cli/src/app.rs index 8305c41b7..3e84cf087 100644 --- a/payjoin-cli/src/app.rs +++ b/payjoin-cli/src/app.rs @@ -506,7 +506,7 @@ impl App { println!("* {}", header); } - socket.write_message(Message::Text("Hello WebSocket".into())).unwrap(); + socket.write_message(Message::Text("receiver".into())).unwrap(); println!("Waiting for messages..."); let msg = socket.read_message().expect("Error reading message"); println!("Received Request, deserializing: {}", msg); diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index b7321db8b..96f30270e 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -26,21 +26,23 @@ async fn main() { let msg = websocket.read_message().unwrap(); println!("Received: {}, awaiting Original PSBT", msg); - let buffered_req = ws_req_buffer.clone().pop().await; - // relay Original PSBT request to receiver via websocket - let post = Message::Text(buffered_req.to_string()); - println!("Received Original PSBT, relaying to receiver via websocket"); - websocket.write_message(post).unwrap(); - - println!("Awaiting Payjoin PSBT from receiver via websocket"); // does this need to be async? break because block? - // TODO await ws client transform Original PSBT into Payjoin PSBT - let msg = websocket.read_message().unwrap(); - let serialized_res = msg.into_text().unwrap(); - println!("Received Payjoin PSBT res {:#?}, relaying to sender via http", serialized_res); - - ws_res_buffer.push(serialized_res).await; - println!("sent to http server via push"); - break; + if msg.to_text().unwrap() == "receiver" { + let buffered_req = ws_req_buffer.clone().pop().await; + // relay Original PSBT request to receiver via websocket + let post = Message::Text(buffered_req.to_string()); + println!("Received Original PSBT, relaying to receiver via websocket"); + websocket.write_message(post).unwrap(); + + println!("Awaiting Payjoin PSBT from receiver via websocket"); // does this need to be async? break because block? + // TODO await ws client transform Original PSBT into Payjoin PSBT + let msg = websocket.read_message().unwrap(); + let serialized_res = msg.into_text().unwrap(); + println!("Received Payjoin PSBT res {:#?}, relaying to sender via http", serialized_res); + + ws_res_buffer.push(serialized_res).await; + println!("sent to http server via push"); + break; + } } }); From bef722235e6763044b24fa30f029c5a7efef1388 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 5 Aug 2023 10:29:35 -0400 Subject: [PATCH 11/14] Init relay tests --- payjoin-relay/src/main.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 96f30270e..725b8b33c 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -96,7 +96,7 @@ async fn handle_http_req( } } -struct Buffer { +pub(crate) struct Buffer { buffer: Arc>, sender: mpsc::Sender<()>, receiver: Arc>>, @@ -144,3 +144,18 @@ impl Buffer { contents } } + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_buffer() { + let buffer = Buffer::new(); + let buffer_clone = buffer.clone(); + tokio::spawn(async move { + buffer_clone.push("test".to_string()).await; + }); + buffer.pop().await; + } +} \ No newline at end of file From 8cb4a835b4bb5bcaf2d7b939ebcda74ae746e673 Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 7 Aug 2023 12:31:38 -0400 Subject: [PATCH 12/14] Test e2e concurrent WebSockets Payjoin --- Cargo.lock | 2649 ------------------------------------- Cargo.toml | 2 +- payjoin-relay/Cargo.toml | 11 +- payjoin-relay/src/main.rs | 351 ++++- payjoin/src/relay.rs | 15 +- 5 files changed, 348 insertions(+), 2680 deletions(-) delete mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 5b45722d9..000000000 --- a/Cargo.lock +++ /dev/null @@ -1,2649 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -dependencies = [ - "memchr", -] - -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anyhow" -version = "1.0.70" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" - -[[package]] -name = "ascii" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" - -[[package]] -name = "async-trait" -version = "0.1.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "bech32" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" - -[[package]] -name = "bip21" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b9532c632b068e45a478f5e309126b6e2ec1dbf0bbd327b73836f33d9a43ede" -dependencies = [ - "bitcoin", - "percent-encoding-rfc3986", -] - -[[package]] -name = "bitcoin" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b36f4c848f6bd9ff208128f08751135846cc23ae57d66ab10a22efff1c675f3c" -dependencies = [ - "base64", - "bech32", - "bitcoin-private", - "bitcoin_hashes", - "hex_lit", - "secp256k1", - "serde", -] - -[[package]] -name = "bitcoin-private" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73290177011694f38ec25e165d0387ab7ea749a4b81cd4c80dae5988229f7a57" - -[[package]] -name = "bitcoin_hashes" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d7066118b13d4b20b23645932dfb3a81ce7e29f95726c2036fa33cd7b092501" -dependencies = [ - "bitcoin-private", - "serde", -] - -[[package]] -name = "bitcoincore-rpc" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6c0ee9354e3dac217db4cb1dd31941073a87fe53c86bcf3eb2b8bc97f00a08" -dependencies = [ - "bitcoin-private", - "bitcoincore-rpc-json", - "jsonrpc 0.14.1", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "bitcoincore-rpc-json" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d30ce6f40fb0a2e8d98522796219282504b7a4b14e2b4c26139a7bea6aec6586" -dependencies = [ - "bitcoin", - "bitcoin-private", - "serde", - "serde_json", -] - -[[package]] -name = "bitcoind" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "395fe8137d13c6859fa68421779cd5a4db8cd7b0ef3122537e56f7a49a0598a9" -dependencies = [ - "anyhow", - "bitcoin_hashes", - "core-rpc", - "flate2", - "log", - "minreq", - "tar", - "tempfile", - "which", - "zip", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "block-buffer" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" -dependencies = [ - "generic-array", -] - -[[package]] -name = "brotli" -version = "3.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor", -] - -[[package]] -name = "brotli-decompressor" -version = "2.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "buf_redux" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b953a6887648bb07a535631f2bc00fbdb2a2216f135552cb3f534ed136b9c07f" -dependencies = [ - "memchr", - "safemem", -] - -[[package]] -name = "bumpalo" -version = "3.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" - -[[package]] -name = "bzip2" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" -dependencies = [ - "bzip2-sys", - "libc", -] - -[[package]] -name = "bzip2-sys" -version = "0.1.11+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - -[[package]] -name = "cc" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" -dependencies = [ - "iana-time-zone", - "num-integer", - "num-traits", - "winapi", -] - -[[package]] -name = "chunked_transfer" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cca491388666e04d7248af3f60f0c40cfb0991c72205595d7c396e3510207d1a" - -[[package]] -name = "clap" -version = "4.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76" -dependencies = [ - "bitflags", - "clap_lex", - "is-terminal", - "strsim", - "termcolor", -] - -[[package]] -name = "clap_lex" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "783fe232adfca04f90f56201b26d79682d4cd2625e0bc7290b95123afe558ade" -dependencies = [ - "os_str_bytes", -] - -[[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "config" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" -dependencies = [ - "async-trait", - "json5", - "lazy_static", - "nom", - "pathdiff", - "ron", - "rust-ini", - "serde", - "serde_json", - "toml", - "yaml-rust", -] - -[[package]] -name = "cookie" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "888604f00b3db336d2af898ec3c1d5d0ddf5e6d462220f2ededc33a87ac4bbd5" -dependencies = [ - "time 0.1.45", -] - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" - -[[package]] -name = "core-rpc" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d77079e1b71c2778d6e1daf191adadcd4ff5ec3ccad8298a79061d865b235b" -dependencies = [ - "bitcoin-private", - "core-rpc-json", - "jsonrpc 0.13.0", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "core-rpc-json" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581898ed9a83f31c64731b1d8ca2dfffcfec14edf1635afacd5234cddbde3a41" -dependencies = [ - "bitcoin", - "bitcoin-private", - "serde", - "serde_json", -] - -[[package]] -name = "cpufeatures" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - -[[package]] -name = "cxx" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90d59d9acd2a682b4e40605a242f6670eaa58c5957471cbf85e8aa6a0b97a5e8" -dependencies = [ - "cc", - "cxxbridge-flags", - "cxxbridge-macro", - "link-cplusplus", -] - -[[package]] -name = "cxx-build" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebfa40bda659dd5c864e65f4c9a2b0aff19bea56b017b9b77c73d3766a453a38" -dependencies = [ - "cc", - "codespan-reporting", - "once_cell", - "proc-macro2", - "quote", - "scratch", - "syn 1.0.107", -] - -[[package]] -name = "cxxbridge-flags" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457ce6757c5c70dc6ecdbda6925b958aae7f959bda7d8fb9bde889e34a09dc03" - -[[package]] -name = "cxxbridge-macro" -version = "1.0.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf883b7aacd7b2aeb2a7b338648ee19f57c140d4ee8e52c68979c6b2f7f2263" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "deflate" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" -dependencies = [ - "adler32", - "gzip-header", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "dlv-list" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" - -[[package]] -name = "either" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" - -[[package]] -name = "encoding_rs" -version = "0.8.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "errno" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "fastrand" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a407cfaa3385c4ae6b23e84623d48c2798d06e3e6a1878f7f59f17b3f86499" -dependencies = [ - "instant", -] - -[[package]] -name = "filetime" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "winapi", -] - -[[package]] -name = "flate2" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" -dependencies = [ - "cfg-if", - "crc32fast", - "libc", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[package]] -name = "form_urlencoded" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "pin-utils", - "slab", -] - -[[package]] -name = "generic-array" -version = "0.14.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gzip-header" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" -dependencies = [ - "crc32fast", -] - -[[package]] -name = "h2" -version = "0.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d357c7ae988e7d2182f7d7871d0b963962420b0678b0997ce7de72001aeab782" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] - -[[package]] -name = "headers" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3e372db8e5c0d213e0cd0b9be18be2aca3d44cf2fe30a9d46a65581cd454584" -dependencies = [ - "base64", - "bitflags", - "bytes", - "headers-core", - "http", - "httpdate", - "mime", - "sha1", -] - -[[package]] -name = "headers-core" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7f66481bfee273957b1f20485a4ff3362987f85b2c236580d81b4eb7a326429" -dependencies = [ - "http", -] - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex_lit" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd" - -[[package]] -name = "http" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes", - "http", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "hyper" -version = "0.14.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab302d72a6f11a3b910431ff93aae7e773078c769f0a3ef15fb9ec692ed147d4" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes", - "hyper", - "native-tls", - "tokio", - "tokio-native-tls", -] - -[[package]] -name = "hyper_serde" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4193ebe2b9ecb7d04dd1a777c25f3294e10310fe5583e1006a3f47779d6c6a01" -dependencies = [ - "cookie", - "headers", - "http", - "hyper", - "mime", - "serde", - "serde_bytes", - "time 0.1.45", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.53" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "winapi", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" -dependencies = [ - "cxx", - "cxx-build", -] - -[[package]] -name = "idna" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "indexmap" -version = "1.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" -dependencies = [ - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "ipnet" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11b0d96e660696543b251e58030cf9787df56da39dab19ad60eae7353040917e" - -[[package]] -name = "is-terminal" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.45.0", -] - -[[package]] -name = "itoa" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" - -[[package]] -name = "js-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "json5" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" -dependencies = [ - "pest", - "pest_derive", - "serde", -] - -[[package]] -name = "jsonrpc" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd8d6b3f301ba426b30feca834a2a18d48d5b54e5065496b5c1b05537bee3639" -dependencies = [ - "base64", - "serde", - "serde_json", -] - -[[package]] -name = "jsonrpc" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8128f36b47411cd3f044be8c1f5cc0c9e24d1d1bfdc45f0a57897b32513053f2" -dependencies = [ - "base64", - "serde", - "serde_json", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.139" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" - -[[package]] -name = "link-cplusplus" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" -dependencies = [ - "cc", -] - -[[package]] -name = "linked-hash-map" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" - -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - -[[package]] -name = "lock_api" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "mime" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" - -[[package]] -name = "mime_guess" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[package]] -name = "minreq" -version = "2.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3de406eeb24aba36ed3829532fa01649129677186b44a49debec0ec574ca7da7" -dependencies = [ - "log", - "once_cell", - "rustls", - "webpki", - "webpki-roots", -] - -[[package]] -name = "mio" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.42.0", -] - -[[package]] -name = "multipart" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182" -dependencies = [ - "buf_redux", - "httparse", - "log", - "mime", - "mime_guess", - "quick-error", - "rand", - "safemem", - "tempfile", - "twoway", -] - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" -dependencies = [ - "memchr", - "minimal-lexical", -] - -[[package]] -name = "num-integer" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-traits" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" -dependencies = [ - "hermit-abi 0.2.6", - "libc", -] - -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - -[[package]] -name = "once_cell" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f0b0d4bf799edbc74508c1e8bf170ff5f41238e5f8225603ca7caaae2b7860" - -[[package]] -name = "openssl" -version = "0.10.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b501e44f11665960c7e7fcf062c7d96a14ade4aa98116c004b2e37b5be7d736c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-src" -version = "111.25.0+1.1.1t" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3173cd3626c43e3854b1b727422a276e568d9ec5fe8cec197822cf52cfb743d6" -dependencies = [ - "cc", -] - -[[package]] -name = "openssl-sys" -version = "0.9.80" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23bbbf7854cd45b83958ebe919f0e8e516793727652e27fda10a8384cfc790b7" -dependencies = [ - "autocfg", - "cc", - "libc", - "openssl-src", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "ordered-multimap" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" -dependencies = [ - "dlv-list", - "hashbrown", -] - -[[package]] -name = "os_str_bytes" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall", - "smallvec", - "windows-sys 0.45.0", -] - -[[package]] -name = "pathdiff" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" - -[[package]] -name = "payjoin" -version = "0.8.1" -dependencies = [ - "bip21", - "bitcoin", - "bitcoind", - "env_logger", - "hyper", - "hyper_serde", - "log", - "rand", - "serde", - "serde_json", - "url", -] - -[[package]] -name = "payjoin-cli" -version = "0.0.1" -dependencies = [ - "anyhow", - "base64", - "bip21", - "bitcoincore-rpc", - "clap", - "config", - "env_logger", - "hyper_serde", - "log", - "payjoin", - "reqwest", - "rouille", - "serde", - "serde_json", - "tungstenite", - "url", -] - -[[package]] -name = "payjoin-relay" -version = "0.0.1" -dependencies = [ - "futures", - "hyper", - "hyper_serde", - "payjoin", - "serde", - "serde_json", - "tokio", - "tungstenite", -] - -[[package]] -name = "percent-encoding" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" - -[[package]] -name = "percent-encoding-rfc3986" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3637c05577168127568a64e9dc5a6887da720efef07b3d9472d45f63ab191166" - -[[package]] -name = "pest" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1403e8401ad5dedea73c626b99758535b342502f8d1e361f4a2dd952749122" -dependencies = [ - "thiserror", - "ucd-trie", -] - -[[package]] -name = "pest_derive" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be99c4c1d2fc2769b1d00239431d711d08f6efedcecb8b6e30707160aee99c15" -dependencies = [ - "pest", - "pest_generator", -] - -[[package]] -name = "pest_generator" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e56094789873daa36164de2e822b3888c6ae4b4f9da555a1103587658c805b1e" -dependencies = [ - "pest", - "pest_meta", - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "pest_meta" -version = "2.5.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6733073c7cff3d8459fda0e42f13a047870242aed8b509fe98000928975f359e" -dependencies = [ - "once_cell", - "pest", - "sha2", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "pkg-config" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro2" -version = "1.0.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quote" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "regex" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.6.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" - -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - -[[package]] -name = "reqwest" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68cc60575865c7831548863cc02356512e3f1dc2f3f82cb837d7fc4cc8f3c97c" -dependencies = [ - "base64", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2", - "http", - "http-body", - "hyper", - "hyper-tls", - "ipnet", - "js-sys", - "log", - "mime", - "native-tls", - "once_cell", - "percent-encoding", - "pin-project-lite", - "serde", - "serde_json", - "serde_urlencoded", - "tokio", - "tokio-native-tls", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "winreg", -] - -[[package]] -name = "ring" -version = "0.16.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" -dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi", -] - -[[package]] -name = "ron" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" -dependencies = [ - "base64", - "bitflags", - "serde", -] - -[[package]] -name = "rouille" -version = "3.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f86e4c51a773f953f02bbab5fd049f004bfd384341d62da2a079aff812ab176" -dependencies = [ - "base64", - "brotli", - "chrono", - "deflate", - "filetime", - "multipart", - "num_cpus", - "percent-encoding", - "rand", - "serde", - "serde_derive", - "serde_json", - "sha1", - "threadpool", - "time 0.3.17", - "tiny_http", - "url", -] - -[[package]] -name = "rust-ini" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - -[[package]] -name = "rustix" -version = "0.36.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.45.0", -] - -[[package]] -name = "rustls" -version = "0.20.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] - -[[package]] -name = "ryu" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" - -[[package]] -name = "safemem" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" - -[[package]] -name = "schannel" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d6731146462ea25d9244b2ed5fd1d716d25c52e4d54aa4fb0f3c4e9854dbe2" -dependencies = [ - "lazy_static", - "windows-sys 0.36.1", -] - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "scratch" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" - -[[package]] -name = "sct" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "secp256k1" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25996b82292a7a57ed3508f052cfff8640d38d32018784acd714758b43da9c8f" -dependencies = [ - "bitcoin_hashes", - "rand", - "secp256k1-sys", - "serde", -] - -[[package]] -name = "secp256k1-sys" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70a129b9e9efbfb223753b9163c4ab3b13cff7fd9c7f010fbac25ab4099fa07e" -dependencies = [ - "cc", -] - -[[package]] -name = "security-framework" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0160a13a177a45bfb43ce71c01580998474f556ad854dcbca936dd2841a5c556" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "serde" -version = "1.0.160" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_bytes" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "416bda436f9aab92e02c8e10d49a15ddd339cea90b6e340fe51ed97abb548294" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.160" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "serde_json" -version = "1.0.96" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "slab" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" -dependencies = [ - "autocfg", -] - -[[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.107" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "tar" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" -dependencies = [ - "filetime", - "libc", - "xattr", -] - -[[package]] -name = "tempfile" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" -dependencies = [ - "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", -] - -[[package]] -name = "termcolor" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", -] - -[[package]] -name = "threadpool" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" -dependencies = [ - "num_cpus", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi", -] - -[[package]] -name = "time" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" -dependencies = [ - "libc", - "num_threads", - "serde", - "time-core", -] - -[[package]] -name = "time-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" - -[[package]] -name = "tiny_http" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389915df6413a2e74fb181895f933386023c71110878cd0825588928e64cdc82" -dependencies = [ - "ascii", - "chunked_transfer", - "httpdate", - "log", -] - -[[package]] -name = "tinyvec" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" - -[[package]] -name = "tokio" -version = "1.28.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94d7b1cfd2aa4011f2de74c2c4c63665e27a71006b0a192dcd2710272e73dfa2" -dependencies = [ - "autocfg", - "bytes", - "libc", - "mio", - "num_cpus", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "socket2", - "tokio-macros", - "windows-sys 0.48.0", -] - -[[package]] -name = "tokio-macros" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.15", -] - -[[package]] -name = "tokio-native-tls" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" -dependencies = [ - "native-tls", - "tokio", -] - -[[package]] -name = "tokio-util" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", - "tracing", -] - -[[package]] -name = "toml" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" -dependencies = [ - "serde", -] - -[[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" -dependencies = [ - "cfg-if", - "pin-project-lite", - "tracing-core", -] - -[[package]] -name = "tracing-core" -version = "0.1.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" -dependencies = [ - "once_cell", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[package]] -name = "tungstenite" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15fba1a6d6bb030745759a9a2a588bfe8490fc8b4751a277db3a0be1c9ebbf67" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "sha1", - "thiserror", - "url", - "utf-8", -] - -[[package]] -name = "twoway" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b11b2b5241ba34be09c3cc85a36e56e48f9888862e19cedf23336d35316ed1" -dependencies = [ - "memchr", -] - -[[package]] -name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "ucd-trie" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e79c4d996edb816c91e4308506774452e55e95c3c9de07b6729e17e15a5ef81" - -[[package]] -name = "unicase" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" -dependencies = [ - "version_check", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" - -[[package]] -name = "unicode-ident" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "untrusted" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" - -[[package]] -name = "url" -version = "2.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" -dependencies = [ - "form_urlencoded", - "idna", - "percent-encoding", -] - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "want" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" -dependencies = [ - "log", - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 1.0.107", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-futures" -version = "0.4.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23639446165ca5a5de86ae1d8896b737ae80319560fbaa4c2887b7da6e7ebd7d" -dependencies = [ - "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.107", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" - -[[package]] -name = "web-sys" -version = "0.3.60" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f" -dependencies = [ - "js-sys", - "wasm-bindgen", -] - -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - -[[package]] -name = "which" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c831fbbee9e129a8cf93e7747a82da9d95ba8e16621cae60ec2cdc849bacb7b" -dependencies = [ - "either", - "libc", - "once_cell", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-sys" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea04155a16a59f9eab786fe12a4a450e75cdb175f9e0d80da1e17db09f55b8d2" -dependencies = [ - "windows_aarch64_msvc 0.36.1", - "windows_i686_gnu 0.36.1", - "windows_i686_msvc 0.36.1", - "windows_x86_64_gnu 0.36.1", - "windows_x86_64_msvc 0.36.1", -] - -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.1", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.0", -] - -[[package]] -name = "windows-targets" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.1", - "windows_aarch64_msvc 0.42.1", - "windows_i686_gnu 0.42.1", - "windows_i686_msvc 0.42.1", - "windows_x86_64_gnu 0.42.1", - "windows_x86_64_gnullvm 0.42.1", - "windows_x86_64_msvc 0.42.1", -] - -[[package]] -name = "windows-targets" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" -dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9bb8c3fd39ade2d67e9874ac4f3db21f0d710bee00fe7cab16949ec184eeaa47" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180e6ccf01daf4c426b846dfc66db1fc518f074baa793aa7d9b9aaeffad6a3b6" - -[[package]] -name = "windows_i686_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7917148b2812d1eeafaeb22a97e4813dfa60a3f8f78ebe204bcc88f12f024" - -[[package]] -name = "windows_i686_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd171b8776c41b97521e5da127a2d86ad280114807d0b2ab1e462bc764d9e1" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.36.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c811ca4a8c853ef420abd8592ba53ddbbac90410fab6903b3e79972a631f7680" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "xattr" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1526bbe5aaeb5eb06885f4d987bcdfa5e23187055de9b83fe00156a821fabc" -dependencies = [ - "libc", -] - -[[package]] -name = "yaml-rust" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" -dependencies = [ - "linked-hash-map", -] - -[[package]] -name = "zip" -version = "0.5.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" -dependencies = [ - "byteorder", - "bzip2", - "crc32fast", - "flate2", - "thiserror", - "time 0.1.45", -] diff --git a/Cargo.toml b/Cargo.toml index b22d99542..55029f988 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,2 @@ [workspace] -members = ["payjoin", "payjoin-cli", "payjoin-relay"] +members = ["payjoin", "payjoin-cli", "payjoin-relay", "payjoin-masque"] diff --git a/payjoin-relay/Cargo.toml b/payjoin-relay/Cargo.toml index 594110e8a..eb9be6e5d 100644 --- a/payjoin-relay/Cargo.toml +++ b/payjoin-relay/Cargo.toml @@ -7,10 +7,19 @@ edition = "2021" [dependencies] futures = "0.3.28" +futures-util = { version = "0.3.28", default-features = false, features = ["sink", "std"] } hyper = { version = "0.14.26", features = ["full"] } hyper_serde = "0.13.0" payjoin = { path = "../payjoin", features = ["relay"] } serde = { version = "1.0.160", features = ["derive"] } serde_json = "1.0.96" tokio = { version = "1.28.2", features = ["full"] } -tungstenite = "0.19.0" \ No newline at end of file +tokio-tungstenite = "0.19.0" +tungstenite = "0.19.0" + +[dev-dependencies] +bitcoind = { version = "0.31.1", features = ["0_21_2"] } +env_logger = "0.9.0" +log = { version = "0.4", features = ["std"] } +payjoin = { path = "../payjoin", features = ["send", "receive", "relay"]} +url = { version= "2.3.1" } \ No newline at end of file diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 725b8b33c..4eaaa0388 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -1,17 +1,17 @@ -use std::net::TcpListener; +use tokio::net::TcpListener; use std::sync::Arc; -use tungstenite::accept; use hyper::Body; use hyper::service::{make_service_fn, service_fn}; use hyper::StatusCode; -use tokio::sync::{mpsc, oneshot, Mutex}; +use tokio::sync::{mpsc, Mutex}; use tungstenite::Message; +use futures_util::{SinkExt, StreamExt}; use payjoin::relay; #[tokio::main] async fn main() { - let server = TcpListener::bind("127.0.0.1:3012").unwrap(); + let server = TcpListener::bind("127.0.0.1:3012").await.unwrap(); println!("REElay listening on ws://127.0.0.1:3012 😡"); let req_buffer = Buffer::new(); let res_buffer = Buffer::new(); @@ -20,29 +20,32 @@ async fn main() { let ws_res_buffer = res_buffer.clone(); tokio::spawn(async move { // run websocket server. On connection, await Original PSBT, relay to http server - for stream in server.incoming() { + while let Ok((stream, _)) = server.accept().await { println!("New ws connection!"); - let mut websocket = accept(stream.unwrap()).unwrap(); - let msg = websocket.read_message().unwrap(); - println!("Received: {}, awaiting Original PSBT", msg); - - if msg.to_text().unwrap() == "receiver" { - let buffered_req = ws_req_buffer.clone().pop().await; - // relay Original PSBT request to receiver via websocket - let post = Message::Text(buffered_req.to_string()); - println!("Received Original PSBT, relaying to receiver via websocket"); - websocket.write_message(post).unwrap(); - - println!("Awaiting Payjoin PSBT from receiver via websocket"); // does this need to be async? break because block? - // TODO await ws client transform Original PSBT into Payjoin PSBT - let msg = websocket.read_message().unwrap(); - let serialized_res = msg.into_text().unwrap(); - println!("Received Payjoin PSBT res {:#?}, relaying to sender via http", serialized_res); - - ws_res_buffer.push(serialized_res).await; - println!("sent to http server via push"); - break; - } + let ws_req_buffer = ws_req_buffer.clone(); + let ws_res_buffer = ws_res_buffer.clone(); + tokio::spawn(async move { + let ws_stream = tokio_tungstenite::accept_async(stream).await.unwrap(); + let (mut write, mut read) = ws_stream.split(); + let msg = read.next().await.unwrap().unwrap(); + println!("Received: {}, awaiting Original PSBT", msg); + if msg.to_text().unwrap() == "receiver" { + let buffered_req = ws_req_buffer.clone().pop().await; + // relay Original PSBT request to receiver via websocket + let post = Message::Text(buffered_req.to_string()); + println!("Received Original PSBT, relaying to receiver via websocket"); + write.send(post).await.unwrap(); + + println!("Awaiting Payjoin PSBT from receiver via websocket"); // does this need to be async? break because block? + // TODO await ws client transform Original PSBT into Payjoin PSBT + let msg = read.next().await.unwrap().unwrap(); + let serialized_res = msg.into_text().unwrap(); + println!("Received Payjoin PSBT res {:#?}, relaying to sender via http", serialized_res); + + ws_res_buffer.push(serialized_res).await; + println!("sent to http server via push"); + } + }); } }); @@ -147,7 +150,22 @@ impl Buffer { #[cfg(test)] mod tests { + use std::collections::HashMap; + use std::str::FromStr; + use super::*; + use bitcoind::bitcoincore_rpc; + use env_logger; + + use bitcoin::psbt::Psbt; + use bitcoin::{Amount, OutPoint}; + use bitcoind::bitcoincore_rpc::Client; + use bitcoind::bitcoincore_rpc::core_rpc_json::AddressType; + use bitcoind::bitcoincore_rpc::RpcApi; + use log::{log_enabled, Level}; + use payjoin::bitcoin::base64; + use payjoin::receive::Error; + use payjoin::{bitcoin, PjUriExt, UriExt}; #[tokio::test] async fn test_buffer() { @@ -158,4 +176,285 @@ mod tests { }); buffer.pop().await; } + + #[tokio::test] + async fn test_concurrent_payjoin() { + let _ = env_logger::try_init(); + // Set up sender & receiver bitcoind wallets + let bitcoind_exe = std::env::var("BITCOIND_EXE") + .ok() + .or_else(|| bitcoind::downloaded_exe_path().ok()) + .expect("version feature or env BITCOIND_EXE is required for tests"); + let mut conf = bitcoind::Conf::default(); + conf.view_stdout = log_enabled!(Level::Debug); + let bitcoind = bitcoind::BitcoinD::with_conf(bitcoind_exe, &conf).unwrap(); + let receiver = bitcoind.create_wallet("receiver").unwrap(); + let receiver_address = + receiver.get_new_address(None, Some(AddressType::Bech32)).unwrap().assume_checked(); + let sender = bitcoind.create_wallet("sender").unwrap(); + let sender_address = + sender.get_new_address(None, Some(AddressType::Bech32)).unwrap().assume_checked(); + bitcoind.client.generate_to_address(1, &receiver_address).unwrap(); + bitcoind.client.generate_to_address(101, &sender_address).unwrap(); + + // # 1. set up relay + let relay = TcpListener::bind("0.0.0.0:0").await.unwrap(); + let relay_addr = relay.local_addr().expect("could not get server address"); + println!("Relay listening on ws://{}", &relay_addr); + let req_buffer = Buffer::new(); + let res_buffer = Buffer::new(); + + let ws_req_buffer = req_buffer.clone(); + let ws_res_buffer = res_buffer.clone(); + let _relay_thread = tokio::spawn(async move { + // run websocket server. On connection, await Original PSBT, relay to http server + println!("starting websocket server"); + while let Ok((stream, _)) = relay.accept().await { + println!("New ws connection!"); + let ws_req_buffer = ws_req_buffer.clone(); + let ws_res_buffer = ws_res_buffer.clone(); + tokio::spawn(async move { + let ws_stream = tokio_tungstenite::accept_async(stream).await.unwrap(); + let (mut write, mut read) = ws_stream.split(); + let msg = read.next().await.unwrap().unwrap(); + println!("Received: {}, awaiting Original PSBT", msg); + + if msg.to_text().unwrap() == "receiver" { + let buffered_req = ws_req_buffer.clone().pop().await; + // relay Original PSBT request to receiver via websocket + let post = Message::Text(buffered_req.to_string()); + println!("Received Original PSBT, relaying to receiver via websocket"); + write.send(post).await.unwrap(); + + println!("Awaiting Payjoin PSBT from receiver via websocket"); + let msg = read.next().await.unwrap().unwrap(); + let serialized_res = msg.into_text().unwrap(); + println!("Received Payjoin PSBT res {:#?}, relaying to sender via http", serialized_res); + + ws_res_buffer.push(serialized_res).await; + println!("sent to http server via push"); + } else { + println!("Received sender request"); + let serialized_req = msg.into_text().unwrap(); + ws_req_buffer.push(serialized_req).await; + + println!("Awaiting response"); + let serialized_res = ws_res_buffer.pop().await; + write.send(Message::Text(serialized_res)).await.unwrap(); + println!("Response returned to sender via websocket"); + } + }); + } + }); + + // receiver + let request_address = receiver.get_new_address(None, Some(AddressType::Bech32)).unwrap(). + assume_checked(); + let request_amount = Amount::from_sat(69420); + let request_mock_pj = "https://localhost:3000".parse::().unwrap(); + let uri = format!("bitcoin:{}?amount={}&pj={}", request_address, request_amount.display_in(bitcoin::amount::Denomination::Bitcoin), request_mock_pj); + let uri = payjoin::Uri::try_from(uri).expect("Failed to create URI from string").assume_checked(); + let uri = uri.check_pj_supported().expect("The provided URI doesn't support Payjoin"); + + let receive_thread = tokio::spawn(async move { + println!("Await relay boot"); + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + println!("receiver thread started"); + let ws_url = format!("ws://{}/socket", &relay_addr); + println!("Enrolling receiver with relay @ {}", &ws_url); + let (ws_stream, _) = tokio_tungstenite::connect_async(ws_url.clone()).await.expect("Can't connect"); + let (mut write, mut read) = ws_stream.split(); + println!("receiver connected"); + write.send(Message::Text("receiver".to_string())).await.unwrap(); + let msg = read.next().await.expect("Error reading message").unwrap(); + let req: relay::Request = serde_json::from_str(&msg.to_text().unwrap()).unwrap(); + let body = std::io::Cursor::new(req.body); + let raw_res_body = handle_payjoin_req(receiver, body, &req.query, req.headers).unwrap(); + write.send(Message::text(raw_res_body)).await.unwrap(); + }); + + // 2. set up sender, receiver bitcoind wallets + let sender_thread = tokio::spawn(async move { + println!("Await receiver relay enrollment"); + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + println!("sender thread started"); + let ws_url = format!("ws://{}/socket", &relay_addr); + let (ws_stream, _) = tokio_tungstenite::connect_async(ws_url.clone()).await.expect("Can't connect"); + let (mut write, mut read) = ws_stream.split(); + // TODO check that mailbox exists on relay + let mut outputs = HashMap::with_capacity(1); + outputs.insert(request_address.to_string(), request_amount); + let options = bitcoincore_rpc::json::WalletCreateFundedPsbtOptions { + lock_unspent: Some(true), + fee_rate: Some(Amount::from_sat(2000)), // sat_per_kwu + ..Default::default() + }; + let psbt = sender + .wallet_create_funded_psbt( + &[], // inputs + &outputs, + None, // locktime + Some(options), + None, + ) + .expect("Failed to create PSBT") + .psbt; + let psbt = sender + .wallet_process_psbt(&psbt, None, None, None) + .expect("Failed to process PSBT") + .psbt; + let psbt = Psbt::from_str(&psbt).expect("Failed to load PSBT from base64"); + println!("Original psbt: {:#?}", psbt); + let pj_params = payjoin::send::Configuration::with_fee_contribution( + payjoin::bitcoin::Amount::from_sat(10000), + None, + ); + let (req, ctx) = uri + .create_pj_request(psbt, pj_params) + .expect("Failed to create payjoin request"); + println!("Sending payjoin request body: {:#?}", req.body); + let mut headers = hyper::HeaderMap::new(); + headers.append("Content-Type", hyper::header::HeaderValue::from_str("text/plain").unwrap()); + headers.append("Content-Length", hyper::header::HeaderValue::from_str(&req.body.len().to_string()).unwrap()); + let serialized_req = serde_json::to_string(&relay::Request { + query: req.url.query().unwrap_or("").to_string(), + headers, + body: req.body, + }).expect("Serialized Request"); + write.send(Message::Text(serialized_req)).await.unwrap(); + let msg = read.next().await.unwrap().unwrap(); + let raw_res_body = msg.to_text().unwrap(); + let psbt = ctx.process_response(&mut raw_res_body.as_bytes()).expect("Failed to process response"); + let psbt = sender + .wallet_process_psbt(&base64::encode(&psbt.serialize()), None, None, None) + .expect("Failed to process PSBT") + .psbt; + let tx = sender + .finalize_psbt(&psbt, Some(true)) + .expect("Failed to finalize PSBT") + .hex + .expect("Incomplete PSBT"); + let txid = sender + .send_raw_transaction(&tx) + .expect("Failed to send raw transaction"); + println!("Sent transaction: {}", txid); + }); + + tokio::try_join!(receive_thread, sender_thread).unwrap(); + } + + fn handle_payjoin_req(receiver: Client, body: impl std::io::Read, query: &str, headers: impl payjoin::receive::Headers) -> Result { + println!("handle_payjoin_post"); + let proposal = payjoin::receive::UncheckedProposal::from_request(body, query, headers)?; + + // in a payment processor where the sender could go offline, this is where you schedule to broadcast the original_tx + let _to_broadcast_in_failure_case = proposal.get_transaction_to_schedule_broadcast(); + + // The network is used for checks later + let network = bitcoin::Network::Regtest; + + // Receive Check 1: Can Broadcast + let proposal = proposal.check_can_broadcast(|tx| { + let raw_tx = bitcoin::consensus::encode::serialize_hex(&tx); + let mempool_results = + receiver + .test_mempool_accept(&[raw_tx]) + .expect("bitcoind error"); + match mempool_results.first() { + Some(result) => Ok(result.allowed), + None => panic!("bitcoind error: no mempool results"), + } + })?; + println!("check1"); + + // Receive Check 2: receiver can't sign for proposal inputs + let proposal = proposal.check_inputs_not_owned(|input| { + if let Ok(address) = bitcoin::Address::from_script(input, network) { + receiver + .get_address_info(&address) + .map(|info| info.is_mine.unwrap_or(false)) + .map_err(|e| Error::Server(e.into())) + } else { + Ok(false) + } + })?; + println!("check2"); + // Receive Check 3: receiver can't sign for proposal inputs + let proposal = proposal.check_no_mixed_input_scripts()?; + println!("check3"); + + // Receive Check 4: have we seen this input before? More of a check for non-interactive i.e. payment processor receivers. + let payjoin = proposal.check_no_inputs_seen_before(|_| { + Ok(false) // assume inputs not seen. this is a test. + })?; + println!("check4"); + + let mut payjoin = payjoin.identify_receiver_outputs(|output_script| { + if let Ok(address) = bitcoin::Address::from_script(output_script, network) { + receiver + .get_address_info(&address) + .map(|info| info.is_mine.unwrap_or(false)) + .map_err(|e| Error::Server(e.into())) + } else { + Ok(false) + } + })?; + + // Select receiver payjoin inputs. + _ = try_contributing_inputs(&mut payjoin, &receiver).expect("Failed to contribute inputs"); + + let receiver_substitute_address = receiver + .get_new_address(None, None) + .map_err(|e| Error::Server(e.into()))? + .assume_checked(); + payjoin.substitute_output_address(receiver_substitute_address); + + let payjoin_proposal_psbt = payjoin.apply_fee(Some(1))?; + + println!("Extracted PSBT: {:#?}", payjoin_proposal_psbt); + // Sign payjoin psbt + let payjoin_base64_string = base64::encode(&payjoin_proposal_psbt.serialize()); + // `wallet_process_psbt` adds available utxo data and finalizes + let payjoin_proposal_psbt = receiver + .wallet_process_psbt(&payjoin_base64_string, None, None, Some(false)) + .map_err(|e| Error::Server(e.into()))? + .psbt; + let payjoin_proposal_psbt = Psbt::from_str(&payjoin_proposal_psbt).unwrap(); + let payjoin_proposal_psbt = payjoin.prepare_psbt(payjoin_proposal_psbt)?; + println!("Receiver's Payjoin proposal PSBT Rsponse: {:#?}", payjoin_proposal_psbt); + + let payload = base64::encode(&payjoin_proposal_psbt.serialize()); + println!("successful response"); + Ok(payload) + } + + fn try_contributing_inputs( + payjoin: &mut payjoin::receive::PayjoinProposal, + receiver: &Client, + ) -> Result<(), ()> { + let available_inputs = receiver + .list_unspent(None, None, None, None, None) + .expect("Failed to list unspent from bitcoind"); + let candidate_inputs: HashMap = available_inputs + .iter() + .map(|i| (i.amount, OutPoint { txid: i.txid, vout: i.vout })) + .collect(); + + let selected_outpoint = payjoin.try_preserving_privacy(candidate_inputs).expect("gg"); + let selected_utxo = available_inputs + .iter() + .find(|i| i.txid == selected_outpoint.txid && i.vout == selected_outpoint.vout) + .expect("This shouldn't happen. Failed to retrieve the privacy preserving utxo from those we provided to the seclector."); + println!("selected utxo: {:#?}", selected_utxo); + + // calculate receiver payjoin outputs given receiver payjoin inputs and original_psbt, + let txo_to_contribute = bitcoin::TxOut { + value: selected_utxo.amount.to_sat(), + script_pubkey: selected_utxo.script_pub_key.clone(), + }; + let outpoint_to_contribute = + bitcoin::OutPoint { txid: selected_utxo.txid, vout: selected_utxo.vout }; + payjoin.contribute_witness_input(txo_to_contribute, outpoint_to_contribute); + Ok(()) + } } \ No newline at end of file diff --git a/payjoin/src/relay.rs b/payjoin/src/relay.rs index 2c4efca14..a7f39a5bc 100644 --- a/payjoin/src/relay.rs +++ b/payjoin/src/relay.rs @@ -1,4 +1,3 @@ - use std::borrow::Cow; use serde::{Deserialize, Serialize}; @@ -7,7 +6,10 @@ use serde::{Deserialize, Serialize}; pub struct Request { pub body: Vec, pub query: String, - #[serde(deserialize_with = "hyper_serde::deserialize", serialize_with = "hyper_serde::serialize")] + #[serde( + deserialize_with = "hyper_serde::deserialize", + serialize_with = "hyper_serde::serialize" + )] pub headers: hyper::HeaderMap, } @@ -30,4 +32,11 @@ impl crate::receive::Headers for VecHeaders<'_> { log::debug!("get_header({})", key); self.0.iter().find(|(k, _)| k.eq_ignore_ascii_case(key)).map(|(_, v)| v.as_ref()) } -} \ No newline at end of file +} + +impl crate::receive::Headers for hyper::HeaderMap { + fn get_header(&self, key: &str) -> Option<&str> { + log::debug!("get_header({})", key); + self.get(key).and_then(|v| v.to_str().ok()) + } +} From 5f2e973f4208a76963a1a2c418e16877a6ba0dd7 Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 7 Aug 2023 14:10:57 -0400 Subject: [PATCH 13/14] Receive payjoin Asynchronously --- payjoin-relay/src/main.rs | 52 ++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 4eaaa0388..785cd529f 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -30,7 +30,7 @@ async fn main() { let msg = read.next().await.unwrap().unwrap(); println!("Received: {}, awaiting Original PSBT", msg); if msg.to_text().unwrap() == "receiver" { - let buffered_req = ws_req_buffer.clone().pop().await; + let buffered_req = ws_req_buffer.clone().peek().await; // relay Original PSBT request to receiver via websocket let post = Message::Text(buffered_req.to_string()); println!("Received Original PSBT, relaying to receiver via websocket"); @@ -82,7 +82,7 @@ async fn handle_http_req( req_buffer.push(serialized_req).await; println!("Relayed req to ws channel from HTTP, awaiting Response"); - let serialized_res = res_buffer.pop().await; + let serialized_res = res_buffer.peek().await; let res = serde_json::from_str::(&serialized_res).unwrap(); println!("POST / response received {:?}", res); let res = hyper::Response::builder() @@ -133,7 +133,7 @@ impl Buffer { let _ = self.sender.send(()).await; // signal that a new request has been added } - async fn pop(&self) -> String { + async fn peek(&self) -> String { let mut buffer = self.buffer.lock().await; let mut contents = buffer.clone(); if contents.is_empty() { @@ -143,7 +143,6 @@ impl Buffer { buffer = self.buffer.lock().await; contents = buffer.clone(); } - *buffer = String::new(); contents } } @@ -174,7 +173,7 @@ mod tests { tokio::spawn(async move { buffer_clone.push("test".to_string()).await; }); - buffer.pop().await; + buffer.peek().await; } #[tokio::test] @@ -220,26 +219,30 @@ mod tests { println!("Received: {}, awaiting Original PSBT", msg); if msg.to_text().unwrap() == "receiver" { - let buffered_req = ws_req_buffer.clone().pop().await; + let buffered_req = ws_req_buffer.clone().peek().await; // relay Original PSBT request to receiver via websocket let post = Message::Text(buffered_req.to_string()); println!("Received Original PSBT, relaying to receiver via websocket"); write.send(post).await.unwrap(); println!("Awaiting Payjoin PSBT from receiver via websocket"); - let msg = read.next().await.unwrap().unwrap(); - let serialized_res = msg.into_text().unwrap(); - println!("Received Payjoin PSBT res {:#?}, relaying to sender via http", serialized_res); - - ws_res_buffer.push(serialized_res).await; - println!("sent to http server via push"); + match read.next().await { + Some(Ok(Message::Text(res))) => { + println!("Received Payjoin PSBT res {:#?}, relaying to sender via http", res); + + ws_res_buffer.push(res).await; + println!("sent to http server via push"); + }, + // close frame or other unexpected response + _ => return, + } } else { println!("Received sender request"); let serialized_req = msg.into_text().unwrap(); ws_req_buffer.push(serialized_req).await; println!("Awaiting response"); - let serialized_res = ws_res_buffer.pop().await; + let serialized_res = ws_res_buffer.peek().await; write.send(Message::Text(serialized_res)).await.unwrap(); println!("Response returned to sender via websocket"); } @@ -262,15 +265,21 @@ mod tests { println!("receiver thread started"); let ws_url = format!("ws://{}/socket", &relay_addr); println!("Enrolling receiver with relay @ {}", &ws_url); - let (ws_stream, _) = tokio_tungstenite::connect_async(ws_url.clone()).await.expect("Can't connect"); - let (mut write, mut read) = ws_stream.split(); + let (mut ws_stream, _) = tokio_tungstenite::connect_async(ws_url.clone()).await.expect("Can't connect"); println!("receiver connected"); - write.send(Message::Text("receiver".to_string())).await.unwrap(); - let msg = read.next().await.expect("Error reading message").unwrap(); + ws_stream.send(Message::Text("receiver".to_string())).await.unwrap(); + println!("receiver sent enrollment"); + ws_stream.close(None).await.unwrap(); + println!("receiver closed connection"); + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + let (mut ws_stream, _) = tokio_tungstenite::connect_async(ws_url.clone()).await.expect("Can't connect"); + ws_stream.send(Message::Text("receiver".to_string())).await.unwrap(); + println!("receiver reconnected"); + let msg = ws_stream.next().await.expect("Error reading message").unwrap(); let req: relay::Request = serde_json::from_str(&msg.to_text().unwrap()).unwrap(); let body = std::io::Cursor::new(req.body); let raw_res_body = handle_payjoin_req(receiver, body, &req.query, req.headers).unwrap(); - write.send(Message::text(raw_res_body)).await.unwrap(); + ws_stream.send(Message::text(raw_res_body)).await.unwrap(); }); // 2. set up sender, receiver bitcoind wallets @@ -279,8 +288,7 @@ mod tests { tokio::time::sleep(std::time::Duration::from_secs(3)).await; println!("sender thread started"); let ws_url = format!("ws://{}/socket", &relay_addr); - let (ws_stream, _) = tokio_tungstenite::connect_async(ws_url.clone()).await.expect("Can't connect"); - let (mut write, mut read) = ws_stream.split(); + let (mut ws_stream, _) = tokio_tungstenite::connect_async(ws_url.clone()).await.expect("Can't connect"); // TODO check that mailbox exists on relay let mut outputs = HashMap::with_capacity(1); outputs.insert(request_address.to_string(), request_amount); @@ -321,8 +329,8 @@ mod tests { headers, body: req.body, }).expect("Serialized Request"); - write.send(Message::Text(serialized_req)).await.unwrap(); - let msg = read.next().await.unwrap().unwrap(); + ws_stream.send(Message::Text(serialized_req)).await.unwrap(); + let msg = ws_stream.next().await.unwrap().unwrap(); let raw_res_body = msg.to_text().unwrap(); let psbt = ctx.process_response(&mut raw_res_body.as_bytes()).expect("Failed to process response"); let psbt = sender From 83e7ee7b8e9f0534e7241c1da9484a561f4f2223 Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 7 Aug 2023 14:17:13 -0400 Subject: [PATCH 14/14] Test sender offline asynchronicity --- payjoin-relay/src/main.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/payjoin-relay/src/main.rs b/payjoin-relay/src/main.rs index 785cd529f..f5b550282 100644 --- a/payjoin-relay/src/main.rs +++ b/payjoin-relay/src/main.rs @@ -329,6 +329,13 @@ mod tests { headers, body: req.body, }).expect("Serialized Request"); + ws_stream.send(Message::Text(serialized_req.clone())).await.unwrap(); + + ws_stream.close(None).await.unwrap(); + + // delay & restart + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + let (mut ws_stream, _) = tokio_tungstenite::connect_async(ws_url.clone()).await.expect("Can't connect"); ws_stream.send(Message::Text(serialized_req)).await.unwrap(); let msg = ws_stream.next().await.unwrap().unwrap(); let raw_res_body = msg.to_text().unwrap();