diff --git a/Cargo.lock b/Cargo.lock index 0c146d63..018377c2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,87 +38,6 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD:Cargo.lock -======= -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-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[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 = "anstream" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" - -[[package]] -name = "anstyle-parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] ->>>>>>> 369866e (Parse receiver response errors):payjoin-cli/Cargo.lock name = "anyhow" version = "1.0.75" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -347,15 +266,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -<<<<<<< HEAD:Cargo.lock version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" -======= -version = "4.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" ->>>>>>> 369866e (Parse receiver response errors):payjoin-cli/Cargo.lock dependencies = [ "atty", "bitflags 1.3.2", @@ -369,17 +282,10 @@ dependencies = [ ] [[package]] -<<<<<<< HEAD:Cargo.lock name = "clap_derive" version = "3.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -======= -name = "clap_builder" -version = "4.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" ->>>>>>> 369866e (Parse receiver response errors):payjoin-cli/Cargo.lock dependencies = [ "heck", "proc-macro-error", @@ -390,24 +296,12 @@ dependencies = [ [[package]] name = "clap_lex" -<<<<<<< HEAD:Cargo.lock version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" dependencies = [ "os_str_bytes", ] -======= -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" ->>>>>>> 369866e (Parse receiver response errors):payjoin-cli/Cargo.lock [[package]] name = "config" @@ -430,9 +324,9 @@ dependencies = [ [[package]] name = "core-foundation" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ "core-foundation-sys", "libc", @@ -440,9 +334,9 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.4" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" +checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" [[package]] name = "core-rpc" @@ -498,6 +392,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "deranged" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +dependencies = [ + "powerfmt", +] + [[package]] name = "digest" version = "0.10.7" @@ -557,14 +460,14 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" [[package]] name = "filetime" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +checksum = "1ee447700ac8aa0b2f2bd7bc4462ad686ba06baa6727ac149a2d6277f0d240fd" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.3.5", - "windows-sys 0.48.0", + "redox_syscall", + "windows-sys 0.52.0", ] [[package]] @@ -832,15 +735,11 @@ dependencies = [ name = "indexmap" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -<<<<<<< HEAD:Cargo.lock checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" dependencies = [ "equivalent", "hashbrown 0.14.3", ] -======= -checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" ->>>>>>> 369866e (Parse receiver response errors):payjoin-cli/Cargo.lock [[package]] name = "itoa" @@ -910,9 +809,9 @@ checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] name = "linux-raw-sys" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" +checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" [[package]] name = "lock_api" @@ -1050,7 +949,7 @@ checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall", "smallvec", "windows-targets 0.48.5", ] @@ -1184,6 +1083,12 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1270,19 +1175,10 @@ checksum = "52c4f3084aa3bc7dfbba4eff4fab2a54db4324965d8872ab933565e6fbd83bc6" dependencies = [ "pem", "ring 0.16.20", - "time 0.3.20", + "time 0.3.30", "yasna", ] -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "redox_syscall" version = "0.4.1" @@ -1294,15 +1190,9 @@ dependencies = [ [[package]] name = "regex" -<<<<<<< HEAD:Cargo.lock -version = "1.9.6" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" -======= -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12de2eff854e5fa4b1295edd650e227e9d8fb0c9e90b12e7f36d6a6811791a29" ->>>>>>> 369866e (Parse receiver response errors):payjoin-cli/Cargo.lock +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" dependencies = [ "aho-corasick", "memchr", @@ -1312,15 +1202,9 @@ dependencies = [ [[package]] name = "regex-automata" -<<<<<<< HEAD:Cargo.lock -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" -======= -version = "0.3.7" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49530408a136e16e5b486e883fbb6ba058e8e4e8ae6621a77b048b314336e629" ->>>>>>> 369866e (Parse receiver response errors):payjoin-cli/Cargo.lock +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" dependencies = [ "aho-corasick", "memchr", @@ -1329,49 +1213,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.7.5" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" -<<<<<<< HEAD:Cargo.lock -======= - -[[package]] -name = "reqwest" -version = "0.11.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde824a14b7c14f85caff81225f411faacc04a2013f41670f41443742b1c1c55" -dependencies = [ - "base64 0.21.2", - "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", -] ->>>>>>> 369866e (Parse receiver response errors):payjoin-cli/Cargo.lock +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "ring" @@ -1390,9 +1234,9 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.6" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" dependencies = [ "cc", "getrandom", @@ -1431,15 +1275,15 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.25" +version = "0.38.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc99bc2d4f1fed22595588a013687477aedf3cdcfb26558c559edb67b4d9b22e" +checksum = "9470c4bf8246c8daf25f9598dca807fb6510347b1e1cfa55749113850c79d88a" dependencies = [ "bitflags 2.4.1", "errno", "libc", "linux-raw-sys", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] @@ -1449,7 +1293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9" dependencies = [ "log", - "ring 0.17.6", + "ring 0.17.7", "rustls-webpki", "sct", ] @@ -1481,7 +1325,7 @@ version = "0.101.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" dependencies = [ - "ring 0.17.6", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -1512,7 +1356,7 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" dependencies = [ - "ring 0.17.6", + "ring 0.17.7", "untrusted 0.9.0", ] @@ -1705,7 +1549,7 @@ checksum = "7ef1adac450ad7f4b3c28589471ade84f25f731a7a0fe30d71dfa9f60fd808e5" dependencies = [ "cfg-if", "fastrand", - "redox_syscall 0.4.1", + "redox_syscall", "rustix", "windows-sys 0.48.0", ] @@ -1758,19 +1602,21 @@ dependencies = [ [[package]] name = "time" -version = "0.3.20" +version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" dependencies = [ + "deranged", + "powerfmt", "serde", "time-core", ] [[package]] name = "time-core" -version = "0.1.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" [[package]] name = "tinyvec" @@ -2250,7 +2096,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e17bb3549cc1321ae1296b9cdc2698e2b6cb1992adfa19a8c72e5b7a738f44cd" dependencies = [ - "time 0.3.20", + "time 0.3.30", ] [[package]] diff --git a/payjoin-cli/src/app.rs b/payjoin-cli/src/app.rs index 308b206d..1e185ff2 100644 --- a/payjoin-cli/src/app.rs +++ b/payjoin-cli/src/app.rs @@ -91,7 +91,8 @@ impl App { let psbt = Psbt::from_str(&psbt).with_context(|| "Failed to load PSBT from base64")?; log::debug!("Original psbt: {:#?}", psbt); let fallback_tx = psbt.clone().extract_tx(); - let (req, ctx) = payjoin::send::RequestBuilder::from_psbt_and_uri(psbt, uri) + let version = 1; + let (req, ctx) = payjoin::send::RequestBuilder::from_psbt_and_uri(psbt, uri, version) .with_context(|| "Failed to build payjoin request")? .build_recommended(fee_rate) .with_context(|| "Failed to build payjoin request")?; @@ -397,13 +398,9 @@ struct OutPointSet(HashSet); use std::fs::OpenOptions; impl OutPointSet { - fn new() -> Self { - Self(HashSet::new()) - } + fn new() -> Self { Self(HashSet::new()) } - fn insert(&mut self, input: bitcoin::OutPoint) -> bool { - self.0.insert(input) - } + fn insert(&mut self, input: bitcoin::OutPoint) -> bool { self.0.insert(input) } } #[derive(Debug, Clone, Deserialize)] @@ -506,7 +503,6 @@ impl payjoin::receive::Headers for Headers<'_> { } } -<<<<<<< HEAD fn serialize_psbt(psbt: &Psbt) -> String { payjoin::base64::encode(&psbt.serialize()) } #[cfg(feature = "danger-local-https")] diff --git a/payjoin/src/lib.rs b/payjoin/src/lib.rs index 284d3ff3..0bf00085 100644 --- a/payjoin/src/lib.rs +++ b/payjoin/src/lib.rs @@ -31,7 +31,7 @@ pub mod send; pub(crate) mod input_type; #[cfg(any(feature = "send", feature = "receive"))] pub(crate) mod psbt; -mod uri; +pub mod uri; #[cfg(any(feature = "send", feature = "receive"))] pub(crate) mod weight; diff --git a/payjoin/src/send/error.rs b/payjoin/src/send/error.rs index ef550a4c..831b1bb2 100644 --- a/payjoin/src/send/error.rs +++ b/payjoin/src/send/error.rs @@ -217,7 +217,7 @@ pub enum ResponseError { /// /// The `WellKnownError` represents `errorCode` and `message`. /// The `String` is a custom message that can be used for debug logs. - WellKnown(WellKnownError, String), + WellKnown(WellKnownError, Option), /// `Unrecognized` errors are errors that are not well known and are only displayed in debug logs. /// They are not displayed to end users. /// @@ -306,7 +306,7 @@ impl<'de> Deserialize<'de> for ResponseError { let well_known_error = WellKnownError::from_str(&res.error_code); if let Ok(wk_error) = well_known_error { - Ok(ResponseError::WellKnown(wk_error, res.message)) + Ok(ResponseError::WellKnown(wk_error, Some(res.message))) } else { Ok(ResponseError::Unrecognized(res.error_code, res.message)) } diff --git a/payjoin/src/send/mod.rs b/payjoin/src/send/mod.rs index 255c6098..a7eedba3 100644 --- a/payjoin/src/send/mod.rs +++ b/payjoin/src/send/mod.rs @@ -158,6 +158,7 @@ mod error; type InternalResult = Result; +#[derive(Debug, Clone)] pub struct RequestBuilder<'a> { psbt: Psbt, uri: PjUri<'a>, @@ -165,6 +166,7 @@ pub struct RequestBuilder<'a> { fee_contribution: Option<(bitcoin::Amount, Option)>, clamp_fee_contribution: bool, min_fee_rate: FeeRate, + version: usize, } impl<'a> RequestBuilder<'a> { @@ -176,15 +178,19 @@ impl<'a> RequestBuilder<'a> { pub fn from_psbt_and_uri( psbt: Psbt, uri: crate::Uri<'a, NetworkChecked>, + version: usize, ) -> Result { let uri = uri .check_pj_supported() .map_err(|_| InternalCreateRequestError::UriDoesNotSupportPayjoin)?; + let disable_output_substitution = uri.extras.disable_output_substitution; + Ok(Self { psbt, uri, // Sender's optional parameters - disable_output_substitution: false, + disable_output_substitution, + version, fee_contribution: None, clamp_fee_contribution: false, min_fee_rate: FeeRate::ZERO, @@ -331,6 +337,7 @@ impl<'a> RequestBuilder<'a> { disable_output_substitution, fee_contribution, self.min_fee_rate, + self.version, ) .map_err(InternalCreateRequestError::Url)?; let body = serialize_psbt(&psbt); @@ -353,6 +360,7 @@ impl<'a> RequestBuilder<'a> { /// /// You need to send this request over HTTP(S) to the receiver. #[non_exhaustive] +#[derive(Debug, Clone)] pub struct Request { /// URL to send the request to. /// @@ -778,9 +786,10 @@ fn serialize_url( disable_output_substitution: bool, fee_contribution: Option<(bitcoin::Amount, usize)>, min_fee_rate: FeeRate, + version: usize, ) -> Result { let mut url = Url::parse(&endpoint)?; - url.query_pairs_mut().append_pair("v", "1"); + url.query_pairs_mut().append_pair("v", &version.to_string()); if disable_output_substitution { url.query_pairs_mut().append_pair("disableoutputsubstitution", "1"); } diff --git a/payjoin/tests/integration.rs b/payjoin/tests/integration.rs index dd4cc545..33b64feb 100644 --- a/payjoin/tests/integration.rs +++ b/payjoin/tests/integration.rs @@ -15,7 +15,7 @@ mod integration { use payjoin::{bitcoin, Error, Uri}; #[test] - fn integration_test() { + fn receiver_return_version_unsupported_known_error() { let _ = env_logger::try_init(); let bitcoind_exe = std::env::var("BITCOIND_EXE") .ok() @@ -77,7 +77,8 @@ mod integration { let psbt = sender.wallet_process_psbt(&psbt, None, None, None).unwrap().psbt; let psbt = Psbt::from_str(&psbt).unwrap(); debug!("Original psbt: {:#?}", psbt); - let (req, ctx) = RequestBuilder::from_psbt_and_uri(psbt, pj_uri) + let version = 2; // fail + let (req, _) = RequestBuilder::from_psbt_and_uri(psbt, pj_uri, version) .unwrap() .build_with_additional_fee( payjoin::bitcoin::Amount::from_sat(10000), @@ -92,6 +93,88 @@ mod integration { // Inside the Receiver: // this data would transit from one party to another over the network in production let response = handle_pj_request(req, headers, receiver); + assert!(response.is_err()) + } + + #[test] + fn integration_test() { + let _ = env_logger::try_init(); + 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(); + + assert_eq!( + Amount::from_btc(50.0).unwrap(), + receiver.get_balances().unwrap().mine.trusted, + "receiver doesn't own bitcoin" + ); + + assert_eq!( + Amount::from_btc(50.0).unwrap(), + sender.get_balances().unwrap().mine.trusted, + "sender doesn't own bitcoin" + ); + + // Receiver creates the payjoin URI + let pj_receiver_address = receiver.get_new_address(None, None).unwrap().assume_checked(); + let amount = Amount::from_btc(1.0).unwrap(); + let pj_uri_string = format!( + "{}?amount={}&pj=https://example.com", + pj_receiver_address.to_qr_uri(), + amount.to_btc() + ); + let pj_uri = Uri::from_str(&pj_uri_string).unwrap(); + let pj_uri = pj_uri.assume_checked(); + // Sender create a funded PSBT (not broadcasted) to address with amount given in the pj_uri + let mut outputs = HashMap::with_capacity(1); + outputs.insert(pj_uri.address.to_string(), pj_uri.amount.unwrap()); + debug!("outputs: {:?}", outputs); + let options = bitcoincore_rpc::json::WalletCreateFundedPsbtOptions { + lock_unspent: Some(true), + fee_rate: Some(payjoin::bitcoin::Amount::from_sat(2000)), + ..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).unwrap().psbt; + let psbt = Psbt::from_str(&psbt).unwrap(); + debug!("Original psbt: {:#?}", psbt); + let version = 1; // pass + let (req, ctx) = RequestBuilder::from_psbt_and_uri(psbt, pj_uri, version) + .unwrap() + .build_with_additional_fee( + payjoin::bitcoin::Amount::from_sat(10000), + None, + bitcoin::FeeRate::ZERO, + false, + ) + .unwrap(); + let headers = HeaderMock::from_vec(&req.body); + + // ********************** + // Inside the Receiver: + // this data would transit from one party to another over the network in production + let response = handle_pj_request(req, headers, receiver).unwrap(); // this response would be returned as http response to the sender // ********************** @@ -130,14 +213,14 @@ mod integration { req: Request, headers: impl Headers, receiver: bitcoincore_rpc::Client, - ) -> String { + ) -> Result { // Receiver receive payjoin proposal, IRL it will be an HTTP request (over ssl or onion) let proposal = payjoin::receive::UncheckedProposal::from_request( req.body.as_slice(), req.url.query().unwrap_or(""), headers, ) - .unwrap(); + .map_err(|e| Error::BadRequest(e))?; // 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.extract_tx_to_schedule_broadcast(); @@ -224,6 +307,6 @@ mod integration { .unwrap(); let psbt = payjoin_proposal.psbt(); debug!("Receiver's Payjoin proposal PSBT: {:#?}", &psbt); - base64::encode(&psbt.serialize()) + Ok(base64::encode(&psbt.serialize())) } }