diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ad93e197b..7cb01417e 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 diff --git a/payjoin-cli/Cargo.lock b/payjoin-cli/Cargo.lock index 88c4df96f..7bf944f46 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-cli/src/app.rs b/payjoin-cli/src/app.rs index 9b8844b7a..dd786c372 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/CHANGELOG.md b/payjoin/CHANGELOG.md index e1c559167..81fa44ec1 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 4a582d16e..254459ce3 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" diff --git a/payjoin/src/receive/mod.rs b/payjoin/src/receive/mod.rs index faab6231f..74a72504a 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() { @@ -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}; @@ -295,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 { @@ -303,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, @@ -368,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() } @@ -376,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. @@ -399,12 +374,20 @@ 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 } } } +/// 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. @@ -440,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. @@ -484,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 @@ -550,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, @@ -814,15 +791,33 @@ 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) } } +/// 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 owned_vouts(&self) -> &Vec { &self.owned_vouts } + + pub fn psbt(&self) -> &Psbt { &self.payjoin_psbt } +} + #[cfg(test)] mod test { use super::*; @@ -846,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| // |-----------------|-----------------------|------------------------------|-------------------------| @@ -864,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"); } @@ -874,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 a1a5cfcdf..c99c05711 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