From a156d89f1e81bc8f706ab253e6164365122ef869 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sun, 22 Dec 2024 23:53:27 -0500 Subject: [PATCH 1/3] Fix unresumable Sender The Sender was generating new hpke keys on resume since keygen was being done in `extract_v2`. This caused a problem where when the Sender was persisted, stopped, and resumed, the receiver would have already pushed a response to a different ShortId than the one the Sender would look for it in the resumed state. By generating a key on Sender creation and persisting that the Sender can produce a consistent HpkeContext every single run. --- payjoin/src/hpke.rs | 5 +++++ payjoin/src/send/mod.rs | 15 +++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/payjoin/src/hpke.rs b/payjoin/src/hpke.rs index b88434f7..aab63c46 100644 --- a/payjoin/src/hpke.rs +++ b/payjoin/src/hpke.rs @@ -31,6 +31,11 @@ impl From for (HpkeSecretKey, HpkePublicKey) { } impl HpkeKeyPair { + pub fn from_secret_key(secret_key: &HpkeSecretKey) -> Self { + let public_key = ::sk_to_pk(&secret_key.0); + Self(secret_key.clone(), HpkePublicKey(public_key)) + } + pub fn gen_keypair() -> Self { let (sk, pk) = ::gen_keypair(&mut OsRng); Self(HpkeSecretKey(sk), HpkePublicKey(pk)) diff --git a/payjoin/src/send/mod.rs b/payjoin/src/send/mod.rs index 6e2eb4a7..a719e962 100644 --- a/payjoin/src/send/mod.rs +++ b/payjoin/src/send/mod.rs @@ -34,7 +34,9 @@ use serde::{Deserialize, Serialize}; use url::Url; #[cfg(feature = "v2")] -use crate::hpke::{decrypt_message_b, encrypt_message_a, HpkeKeyPair, HpkePublicKey}; +use crate::hpke::{ + decrypt_message_b, encrypt_message_a, HpkeKeyPair, HpkePublicKey, HpkeSecretKey, +}; #[cfg(feature = "v2")] use crate::ohttp::{ohttp_decapsulate, ohttp_encapsulate}; use crate::psbt::PsbtExt; @@ -228,6 +230,8 @@ impl<'a> SenderBuilder<'a> { fee_contribution, payee, min_fee_rate: self.min_fee_rate, + #[cfg(feature = "v2")] + reply_key: HpkeKeyPair::gen_keypair().0, }) } } @@ -246,6 +250,8 @@ pub struct Sender { min_fee_rate: FeeRate, /// Script of the person being paid payee: ScriptBuf, + #[cfg(feature = "v2")] + reply_key: HpkeSecretKey, } impl Sender { @@ -298,7 +304,7 @@ impl Sender { self.fee_contribution, self.min_fee_rate, )?; - let hpke_ctx = HpkeContext::new(rs); + let hpke_ctx = HpkeContext::new(rs, &self.reply_key); let body = encrypt_message_a( body, &hpke_ctx.reply_pair.public_key().clone(), @@ -475,8 +481,8 @@ struct HpkeContext { #[cfg(feature = "v2")] impl HpkeContext { - pub fn new(receiver: HpkePublicKey) -> Self { - Self { receiver, reply_pair: HpkeKeyPair::gen_keypair() } + pub fn new(receiver: HpkePublicKey, reply_key: &HpkeSecretKey) -> Self { + Self { receiver, reply_pair: HpkeKeyPair::from_secret_key(reply_key) } } } @@ -975,6 +981,7 @@ mod test { fee_contribution: None, min_fee_rate: FeeRate::ZERO, payee: ScriptBuf::from(vec![0x00]), + reply_key: HpkeKeyPair::gen_keypair().0, }; let serialized = serde_json::to_string(&req_ctx).unwrap(); let deserialized = serde_json::from_str(&serialized).unwrap(); From 226816a7e095a1f2dea92242532ba94f54010e8b Mon Sep 17 00:00:00 2001 From: DanGould Date: Mon, 23 Dec 2024 00:26:28 -0500 Subject: [PATCH 2/3] Release payjoin-0.22.0 --- Cargo-minimal.lock | 2 +- Cargo-recent.lock | 2 +- payjoin-cli/Cargo.toml | 2 +- payjoin/CHANGELOG.md | 5 +++++ payjoin/Cargo.toml | 2 +- 5 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 304884b5..f9fbea2e 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -1577,7 +1577,7 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "payjoin" -version = "0.21.0" +version = "0.22.0" dependencies = [ "bhttp", "bitcoin", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index 304884b5..f9fbea2e 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -1577,7 +1577,7 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "payjoin" -version = "0.21.0" +version = "0.22.0" dependencies = [ "bhttp", "bitcoin", diff --git a/payjoin-cli/Cargo.toml b/payjoin-cli/Cargo.toml index 225cf652..bd7fa1fa 100644 --- a/payjoin-cli/Cargo.toml +++ b/payjoin-cli/Cargo.toml @@ -37,7 +37,7 @@ hyper = { version = "1", features = ["http1", "server"], optional = true } hyper-rustls = { version = "0.26", optional = true } hyper-util = { version = "0.1", optional = true } log = "0.4.7" -payjoin = { version = "0.21.0", features = ["send", "receive", "base64"] } +payjoin = { version = "0.22.0", features = ["send", "receive", "base64"] } rcgen = { version = "0.11.1", optional = true } reqwest = { version = "0.12", default-features = false } rustls = { version = "0.22.4", optional = true } diff --git a/payjoin/CHANGELOG.md b/payjoin/CHANGELOG.md index 95218c11..9b325d70 100644 --- a/payjoin/CHANGELOG.md +++ b/payjoin/CHANGELOG.md @@ -1,5 +1,10 @@ # Payjoin Changelog +## 0.22.0 + +- Propagate Uri Fragment parameter errors to the caller +- Have `Sender` to persist reply key so resumption listens where a previous sender left off + ## 0.21.0 - Upgrade rustls v0.22.4 diff --git a/payjoin/Cargo.toml b/payjoin/Cargo.toml index 541bd864..de01a1f7 100644 --- a/payjoin/Cargo.toml +++ b/payjoin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "payjoin" -version = "0.21.0" +version = "0.22.0" authors = ["Dan Gould "] description = "Payjoin Library for the BIP78 Pay to Endpoint protocol." repository = "https://github.com/payjoin/rust-payjoin" From 86a9a8a8c775ab1e37457a8764e314bb474ee3a1 Mon Sep 17 00:00:00 2001 From: spacebear Date: Mon, 23 Dec 2024 11:51:43 +0100 Subject: [PATCH 3/3] Fix e2e test Bubble up payjoin-cli errors instead of silently swallowing them. --- payjoin-cli/tests/e2e.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/payjoin-cli/tests/e2e.rs b/payjoin-cli/tests/e2e.rs index e9caa23c..439dd90b 100644 --- a/payjoin-cli/tests/e2e.rs +++ b/payjoin-cli/tests/e2e.rs @@ -293,7 +293,7 @@ mod e2e { .stderr(Stdio::inherit()) .spawn() .expect("Failed to execute payjoin-cli"); - let _ = send_until_request_timeout(cli_send_initiator).await; + send_until_request_timeout(cli_send_initiator).await?; let cli_receive_resumer = Command::new(payjoin_cli) .arg("--rpchost") @@ -309,7 +309,7 @@ mod e2e { .stderr(Stdio::inherit()) .spawn() .expect("Failed to execute payjoin-cli"); - let _ = respond_with_payjoin(cli_receive_resumer).await; + respond_with_payjoin(cli_receive_resumer).await?; let cli_send_resumer = Command::new(payjoin_cli) .arg("--rpchost") @@ -328,7 +328,7 @@ mod e2e { .stderr(Stdio::inherit()) .spawn() .expect("Failed to execute payjoin-cli"); - let _ = check_payjoin_sent(cli_send_resumer).await; + check_payjoin_sent(cli_send_resumer).await?; Ok(()) }