From 28568971c18d8a8f2933033902b69d42ebf1787f Mon Sep 17 00:00:00 2001 From: DanGould Date: Tue, 26 Sep 2023 22:09:27 -0400 Subject: [PATCH 1/5] Pin rustls 0.20.8 for MSRV 1.57.0 --- .github/workflows/rust.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ad93e197..7cb01417 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -31,7 +31,7 @@ jobs: cargo update -p log --precise 0.4.18 cargo update -p tempfile --precise 3.6.0 cargo update -p flate2 --precise 1.0.26 - cargo update -p minreq --precise 2.8.0 + cargo update -p rustls --precise 0.20.8 - name: test run: cargo test --verbose --all-features --lib @@ -74,4 +74,4 @@ jobs: - name: fmt check run: | cd ${{ matrix.package }} - cargo fmt --all -- --check \ No newline at end of file + cargo fmt --all -- --check From db2a79c44d5faa98f29d8dd1123d3dbcfa02a792 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 30 Sep 2023 14:11:40 -0400 Subject: [PATCH 2/5] Clean up clippy and format errors --- payjoin/src/receive/mod.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/payjoin/src/receive/mod.rs b/payjoin/src/receive/mod.rs index faab6231..9b299d76 100644 --- a/payjoin/src/receive/mod.rs +++ b/payjoin/src/receive/mod.rs @@ -268,7 +268,6 @@ use std::cmp::{max, min}; use std::collections::{BTreeMap, HashMap}; -use std::str::FromStr; use bitcoin::psbt::Psbt; use bitcoin::{Amount, FeeRate, OutPoint, Script, TxOut}; @@ -814,11 +813,8 @@ impl ProvisionalProposal { min_feerate_sat_per_vb: Option, ) -> Result { let psbt = self.apply_fee(min_feerate_sat_per_vb)?; - let psbt = wallet_process_psbt(psbt).map_err(|e| { - log::error!("wallet_process_psbt error"); - Error::from(e) - })?; - let payjoin_proposal = self.prepare_psbt(psbt).map_err(RequestError::from)?; + let psbt = wallet_process_psbt(psbt)?; + let payjoin_proposal = self.prepare_psbt(psbt)?; Ok(payjoin_proposal) } } From 382bcb3f1ad7690751f2a135db66c62138c8de27 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 30 Sep 2023 14:14:17 -0400 Subject: [PATCH 3/5] Order receive structs by typestate flow --- payjoin/src/receive/mod.rs | 89 +++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/payjoin/src/receive/mod.rs b/payjoin/src/receive/mod.rs index 9b299d76..75afba39 100644 --- a/payjoin/src/receive/mod.rs +++ b/payjoin/src/receive/mod.rs @@ -302,30 +302,6 @@ pub struct UncheckedProposal { params: Params, } -/// Typestate to validate that the Original PSBT has no receiver-owned inputs. -/// -/// Call [`check_no_receiver_owned_inputs()`](struct.UncheckedProposal.html#method.check_no_receiver_owned_inputs) to proceed. -pub struct MaybeInputsOwned { - psbt: Psbt, - params: Params, -} - -/// Typestate to validate that the Original PSBT has no mixed input types. -/// -/// Call [`check_no_mixed_input_types`](struct.UncheckedProposal.html#method.check_no_mixed_input_scripts) to proceed. -pub struct MaybeMixedInputScripts { - psbt: Psbt, - params: Params, -} - -/// Typestate to validate that the Original PSBT has no inputs that have been seen before. -/// -/// Call [`check_no_inputs_seen`](struct.MaybeInputsSeen.html#method.check_no_inputs_seen_before) to proceed. -pub struct MaybeInputsSeen { - psbt: Psbt, - params: Params, -} - impl UncheckedProposal { pub fn from_request( mut body: impl std::io::Read, @@ -404,6 +380,14 @@ impl UncheckedProposal { } } +/// Typestate to validate that the Original PSBT has no receiver-owned inputs. +/// +/// Call [`check_no_receiver_owned_inputs()`](struct.UncheckedProposal.html#method.check_no_receiver_owned_inputs) to proceed. +pub struct MaybeInputsOwned { + psbt: Psbt, + params: Params, +} + impl MaybeInputsOwned { /// Check that the Original PSBT has no receiver-owned inputs. /// Return original-psbt-rejected error or otherwise refuse to sign undesirable inputs. @@ -439,6 +423,14 @@ impl MaybeInputsOwned { } } +/// Typestate to validate that the Original PSBT has no mixed input types. +/// +/// Call [`check_no_mixed_input_types`](struct.UncheckedProposal.html#method.check_no_mixed_input_scripts) to proceed. +pub struct MaybeMixedInputScripts { + psbt: Psbt, + params: Params, +} + impl MaybeMixedInputScripts { /// Verify the original transaction did not have mixed input types /// Call this after checking downstream. @@ -483,6 +475,13 @@ impl MaybeMixedInputScripts { } } +/// Typestate to validate that the Original PSBT has no inputs that have been seen before. +/// +/// Call [`check_no_inputs_seen`](struct.MaybeInputsSeen.html#method.check_no_inputs_seen_before) to proceed. +pub struct MaybeInputsSeen { + psbt: Psbt, + params: Params, +} impl MaybeInputsSeen { /// Make sure that the original transaction inputs have never been seen before. /// This prevents probing attacks. This prevents reentrant Payjoin, where a sender @@ -549,27 +548,6 @@ impl OutputsUnknown { } } -/// A mutable checked proposal that the receiver may contribute inputs to to make a payjoin. -pub struct PayjoinProposal { - payjoin_psbt: Psbt, - params: Params, - owned_vouts: Vec, -} - -impl PayjoinProposal { - pub fn utxos_to_be_locked(&self) -> impl '_ + Iterator { - self.payjoin_psbt.unsigned_tx.input.iter().map(|input| &input.previous_output) - } - - pub fn is_output_substitution_disabled(&self) -> bool { - self.params.disable_output_substitution - } - - pub fn get_owned_vouts(&self) -> &Vec { &self.owned_vouts } - - pub fn psbt(&self) -> &Psbt { &self.payjoin_psbt } -} - /// A mutable checked proposal that the receiver may contribute inputs to to make a payjoin. pub struct ProvisionalProposal { original_psbt: Psbt, @@ -819,6 +797,27 @@ impl ProvisionalProposal { } } +/// A mutable checked proposal that the receiver may contribute inputs to to make a payjoin. +pub struct PayjoinProposal { + payjoin_psbt: Psbt, + params: Params, + owned_vouts: Vec, +} + +impl PayjoinProposal { + pub fn utxos_to_be_locked(&self) -> impl '_ + Iterator { + self.payjoin_psbt.unsigned_tx.input.iter().map(|input| &input.previous_output) + } + + pub fn is_output_substitution_disabled(&self) -> bool { + self.params.disable_output_substitution + } + + pub fn get_owned_vouts(&self) -> &Vec { &self.owned_vouts } + + pub fn psbt(&self) -> &Psbt { &self.payjoin_psbt } +} + #[cfg(test)] mod test { use super::*; From 7474c21a585854f27982daa46d19d026bcc8f998 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 30 Sep 2023 14:21:29 -0400 Subject: [PATCH 4/5] Fix #104 getter names follow convention --- payjoin-cli/src/app.rs | 2 +- payjoin/src/receive/mod.rs | 18 +++++++++--------- payjoin/tests/integration.rs | 2 +- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/payjoin-cli/src/app.rs b/payjoin-cli/src/app.rs index 9b8844b7..dd786c37 100644 --- a/payjoin-cli/src/app.rs +++ b/payjoin-cli/src/app.rs @@ -250,7 +250,7 @@ impl App { )?; // 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(); + let _to_broadcast_in_failure_case = proposal.extract_tx_to_schedule_broadcast(); // The network is used for checks later let network = diff --git a/payjoin/src/receive/mod.rs b/payjoin/src/receive/mod.rs index 75afba39..74a72504 100644 --- a/payjoin/src/receive/mod.rs +++ b/payjoin/src/receive/mod.rs @@ -107,7 +107,7 @@ //! //! ``` //! // 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(); +//! let _to_broadcast_in_failure_case = proposal.extract_tx_to_schedule_broadcast(); //! //! // The network is used for checks later //! let network = match bitcoind.get_blockchain_info()?.chain.as_str() { @@ -294,7 +294,7 @@ pub trait Headers { /// [`UncheckedProposal::from_request()`](crate::receive::UncheckedProposal::from_request()). /// /// If you are implementing an interactive payment processor, you should get extract the original -/// transaction with get_transaction_to_schedule_broadcast() and schedule, followed by checking +/// transaction with extract_tx_to_schedule_broadcast() and schedule, followed by checking /// that the transaction can be broadcast with check_can_broadcast. Otherwise it is safe to /// call assume_interactive_receive to proceed with validation. pub struct UncheckedProposal { @@ -343,7 +343,7 @@ impl UncheckedProposal { } /// The Sender's Original PSBT - pub fn get_transaction_to_schedule_broadcast(&self) -> bitcoin::Transaction { + pub fn extract_tx_to_schedule_broadcast(&self) -> bitcoin::Transaction { self.psbt.clone().extract_tx() } @@ -351,7 +351,7 @@ impl UncheckedProposal { /// /// Receiver MUST check that the Original PSBT from the sender /// can be broadcast, i.e. `testmempoolaccept` bitcoind rpc returns { "allowed": true,.. } - /// for `get_transaction_to_check_broadcast()` before calling this method. + /// for `extract_tx_to_sheculed_broadcast()` before calling this method. /// /// Do this check if you generate bitcoin uri to receive Payjoin on sender request without manual human approval, like a payment processor. /// Such so called "non-interactive" receivers are otherwise vulnerable to probing attacks. @@ -374,7 +374,7 @@ impl UncheckedProposal { /// requires manual intervention, as in most consumer wallets. /// /// So-called "non-interactive" receivers, like payment processors, that allow arbitrary requests are otherwise vulnerable to probing attacks. - /// Those receivers call `get_transaction_to_check_broadcast()` and `attest_tested_and_scheduled_broadcast()` after making those checks downstream. + /// Those receivers call `extract_tx_to_check_broadcast()` and `attest_tested_and_scheduled_broadcast()` after making those checks downstream. pub fn assume_interactive_receiver(self) -> MaybeInputsOwned { MaybeInputsOwned { psbt: self.psbt, params: self.params } } @@ -813,7 +813,7 @@ impl PayjoinProposal { self.params.disable_output_substitution } - pub fn get_owned_vouts(&self) -> &Vec { &self.owned_vouts } + pub fn owned_vouts(&self) -> &Vec { &self.owned_vouts } pub fn psbt(&self) -> &Psbt { &self.payjoin_psbt } } @@ -841,7 +841,7 @@ mod test { } } - fn get_proposal_from_test_vector() -> Result { + fn proposal_from_test_vector() -> Result { // OriginalPSBT Test Vector from BIP // | InputScriptType | Orginal PSBT Fee rate | maxadditionalfeecontribution | additionalfeeoutputindex| // |-----------------|-----------------------|------------------------------|-------------------------| @@ -859,7 +859,7 @@ mod test { #[test] fn can_get_proposal_from_request() { - let proposal = get_proposal_from_test_vector(); + let proposal = proposal_from_test_vector(); assert!(proposal.is_ok(), "OriginalPSBT should be a valid request"); } @@ -869,7 +869,7 @@ mod test { use bitcoin::{Address, Network}; - let proposal = get_proposal_from_test_vector().unwrap(); + let proposal = proposal_from_test_vector().unwrap(); let mut payjoin = proposal .assume_interactive_receiver() .check_inputs_not_owned(|_| Ok(false)) diff --git a/payjoin/tests/integration.rs b/payjoin/tests/integration.rs index a1a5cfcd..c99c0571 100644 --- a/payjoin/tests/integration.rs +++ b/payjoin/tests/integration.rs @@ -137,7 +137,7 @@ mod integration { .unwrap(); // 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(); + let _to_broadcast_in_failure_case = proposal.extract_tx_to_schedule_broadcast(); // Receive Check 1: Can Broadcast let proposal = proposal From 96a48fa9f2ff5fdf379c82e253aa18d856e0f4d4 Mon Sep 17 00:00:00 2001 From: DanGould Date: Sat, 30 Sep 2023 14:08:55 -0400 Subject: [PATCH 5/5] Mark payjoin-0.10.0 release --- payjoin-cli/Cargo.lock | 2 +- payjoin/CHANGELOG.md | 7 +++++++ payjoin/Cargo.toml | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/payjoin-cli/Cargo.lock b/payjoin-cli/Cargo.lock index 88c4df96..7bf944f4 100644 --- a/payjoin-cli/Cargo.lock +++ b/payjoin-cli/Cargo.lock @@ -1124,7 +1124,7 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "payjoin" -version = "0.9.0" +version = "0.10.0" dependencies = [ "bip21", "bitcoin", diff --git a/payjoin/CHANGELOG.md b/payjoin/CHANGELOG.md index e1c55916..81fa44ec 100644 --- a/payjoin/CHANGELOG.md +++ b/payjoin/CHANGELOG.md @@ -1,5 +1,12 @@ # Payjoin Changelog +## 0.10.0 + +- Export `base64` with feature by @jbesraa in #102 +- Improve `receive` api with `ProvisionalProposal`by @jbesraa in #90 +- Document `create_pj_request` by @jbesraa in #87 +- Add BIP 78 reccommended fee `Configuration` by @DanGould in #86 + ## 0.9.0 Bumping `bitcoin` and other crates was a breaking api change. This is a 0.8.1 semver re-release. diff --git a/payjoin/Cargo.toml b/payjoin/Cargo.toml index 4a582d16..254459ce 100644 --- a/payjoin/Cargo.toml +++ b/payjoin/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "payjoin" -version = "0.9.0" +version = "0.10.0" authors = ["Dan Gould "] description = "Payjoin Library for the BIP78 Pay to Endpoint protocol." repository = "https://github.com/payjoin/rust-payjoin"