diff --git a/payjoin-cli/src/app.rs b/payjoin-cli/src/app.rs index dd786c372..3f4537734 100644 --- a/payjoin-cli/src/app.rs +++ b/payjoin-cli/src/app.rs @@ -9,9 +9,9 @@ use bitcoincore_rpc::jsonrpc::serde_json; use bitcoincore_rpc::RpcApi; use clap::ArgMatches; use config::{Config, File, FileFormat}; +use payjoin::bitcoin; use payjoin::bitcoin::psbt::Psbt; use payjoin::receive::{Error, ProvisionalProposal}; -use payjoin::{bitcoin, PjUriExt, UriExt}; use rouille::{Request, Response}; use serde::{Deserialize, Serialize}; @@ -42,21 +42,15 @@ impl App { } pub fn send_payjoin(&self, bip21: &str) -> Result<()> { - use payjoin::send::Configuration; - - let link = payjoin::Uri::try_from(bip21) - .map_err(|e| anyhow!("Failed to create URI from BIP21: {}", e))?; - - let link = link - .assume_checked() - .check_pj_supported() - .map_err(|e| anyhow!("The provided URI doesn't support payjoin (BIP78): {}", e))?; + let uri = payjoin::Uri::try_from(bip21) + .map_err(|e| anyhow!("Failed to create URI from BIP21: {}", e))? + .assume_checked(); - let amount = link.amount.ok_or_else(|| anyhow!("please specify the amount in the Uri"))?; + let amount = uri.amount.ok_or_else(|| anyhow!("please specify the amount in the Uri"))?; // wallet_create_funded_psbt requires a HashMap let mut outputs = HashMap::with_capacity(1); - outputs.insert(link.address.to_string(), amount); + outputs.insert(uri.address.to_string(), amount); // TODO: make payjoin-cli send feerate configurable // 2.1 sat/vB == 525 sat/kwu for testing purposes. @@ -89,14 +83,11 @@ impl App { let psbt = Psbt::from_str(&psbt).with_context(|| "Failed to load PSBT from base64")?; log::debug!("Original psbt: {:#?}", psbt); - let payout_scripts = std::iter::once(link.address.script_pubkey()); - // recommendation or bust for this simple reference implementation - let pj_params = Configuration::recommended(&psbt, payout_scripts, fee_rate) - .unwrap_or_else(|_| Configuration::non_incentivizing()); + let (req, ctx) = payjoin::send::RequestBuilder::from_psbt_and_uri(psbt, uri) + .with_context(|| "Failed to build payjoin request")? + .build_recommended(fee_rate) + .with_context(|| "Failed to build payjoin request")?; - let (req, ctx) = link - .create_pj_request(psbt, pj_params) - .with_context(|| "Failed to create payjoin request")?; let client = reqwest::blocking::Client::builder() .danger_accept_invalid_certs(self.config.danger_accept_invalid_certs) .build() @@ -141,12 +132,10 @@ impl App { 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))?; - let _pj_uri = pj_uri - .assume_checked() - .check_pj_supported() - .map_err(|e| anyhow!("Constructed URI does not support payjoin: {}", e))?; + // check that the URI is corrctly formatted + let _pj_uri = Uri::from_str(&pj_uri_string) + .map_err(|e| anyhow!("Constructed a bad URI string from args: {}", e))? + .assume_checked(); println!( "Listening at {}. Configured to accept payjoin at BIP 21 Payjoin Uri:", @@ -231,10 +220,8 @@ impl App { }; let uri = payjoin::Uri::try_from(uri_string.clone()) .map_err(|_| Error::Server(anyhow!("Could not parse payjoin URI string.").into()))?; - let _ = uri - .assume_checked() // we just got it from bitcoind above - .check_pj_supported() - .map_err(|_| Error::Server(anyhow!("Created bip21 with invalid &pj=.").into()))?; + let _ = uri.assume_checked(); // we just got it from bitcoind above + Ok(Response::text(uri_string)) } diff --git a/payjoin/src/lib.rs b/payjoin/src/lib.rs index 322b6ddd4..284d3ff38 100644 --- a/payjoin/src/lib.rs +++ b/payjoin/src/lib.rs @@ -37,4 +37,4 @@ pub(crate) mod weight; #[cfg(feature = "base64")] pub use bitcoin::base64; -pub use uri::{PjParseError, PjUri, PjUriExt, Uri, UriExt}; +pub use uri::{PjParseError, PjUri, Uri}; diff --git a/payjoin/src/send/error.rs b/payjoin/src/send/error.rs index 1c7cdea5c..8aeebfc5d 100644 --- a/payjoin/src/send/error.rs +++ b/payjoin/src/send/error.rs @@ -127,20 +127,6 @@ impl std::error::Error for ValidationError { } } -#[derive(Debug)] -pub struct ConfigurationError(InternalConfigurationError); - -#[derive(Debug)] -pub(crate) enum InternalConfigurationError { - PrevTxOut(crate::psbt::PrevTxOutError), - InputType(crate::input_type::InputTypeError), - NoInputs, -} - -impl From for ConfigurationError { - fn from(value: InternalConfigurationError) -> Self { ConfigurationError(value) } -} - /// Error returned when request could not be created. /// /// This error can currently only happen due to programmer mistake. @@ -163,6 +149,9 @@ pub(crate) enum InternalCreateRequestError { ChangeIndexOutOfBounds, ChangeIndexPointsAtPayee, Url(url::ParseError), + UriDoesNotSupportPayjoin, + PrevTxOut(crate::psbt::PrevTxOutError), + InputType(crate::input_type::InputTypeError), } impl fmt::Display for CreateRequestError { @@ -182,6 +171,9 @@ impl fmt::Display for CreateRequestError { ChangeIndexOutOfBounds => write!(f, "fee output index is points out of bounds"), ChangeIndexPointsAtPayee => write!(f, "fee output index is points at output belonging to the payee"), Url(e) => write!(f, "cannot parse endpoint url: {:#?}", e), + UriDoesNotSupportPayjoin => write!(f, "the URI does not support payjoin"), + PrevTxOut(e) => write!(f, "invalid previous transaction output: {}", e), + InputType(e) => write!(f, "invalid input type: {}", e), } } } @@ -203,6 +195,9 @@ impl std::error::Error for CreateRequestError { ChangeIndexOutOfBounds => None, ChangeIndexPointsAtPayee => None, Url(error) => Some(error), + UriDoesNotSupportPayjoin => None, + PrevTxOut(error) => Some(error), + InputType(error) => Some(error), } } } diff --git a/payjoin/src/send/mod.rs b/payjoin/src/send/mod.rs index 411ac002b..e06fc1c1d 100644 --- a/payjoin/src/send/mod.rs +++ b/payjoin/src/send/mod.rs @@ -6,7 +6,7 @@ //! 1. Parse BIP21 as [`payjoin::Uri`](crate::Uri) //! 2. Construct URI request parameters, a finalized “Original PSBT” paying .amount to .address //! 3. (optional) Spawn a thread or async task that will broadcast the original PSBT fallback after delay (e.g. 1 minute) unless canceled -//! 4. Construct the request [`PjUriExt::create_pj_request()`](crate::PjUriExt::create_pj_request()) with the PSBT and your parameters +//! 4. Construct the request using [`RequestBuilder`](crate::send::RequestBuilder) with the PSBT and payjoin uri //! 5. Send the request and receive response //! 6. Process the response with [`Context::process_response()`](crate::send::Context::process_response()) //! 7. Sign and finalize the Payjoin Proposal PSBT @@ -23,19 +23,16 @@ //! Start by parsing a valid BIP 21 uri having the `pj` parameter. This is the [`bip21`](https://crates.io/crates/bip21) crate under the hood. //! //! ``` -//! let link = payjoin::Uri::try_from(bip21) -//! .map_err(|e| anyhow!("Failed to create URI from BIP21: {}", e))?; -//! -//! let link = link -//! .check_pj_supported() -//! .map_err(|e| anyhow!("The provided URI doesn't support payjoin (BIP78): {}", e))?; +//! let uri = payjoin::Uri::try_from(bip21) +//! .map_err(|e| anyhow!("Failed to create URI from BIP21: {}", e))? +//! .assume_checked(); // assume bitcoin address is for the right network //! ``` //! //! ### 2. Construct URI request parameters, a finalized "Original PSBT" paying `.amount` to `.address` //! //! ``` //! let mut outputs = HashMap::with_capacity(1); -//! outputs.insert(link.address.to_string(), amount); +//! outputs.insert(uri.address.to_string(), amount); //! //! let options = bitcoincore_rpc::json::WalletCreateFundedPsbtOptions { //! lock_unspent: Some(true), @@ -60,10 +57,6 @@ //! let psbt = Psbt::from_str(&psbt) // SHOULD BE PROVIDED BY CRATE AS HELPER USING rust-bitcoin base64 feature //! .with_context(|| "Failed to load PSBT from base64")?; //! log::debug!("Original psbt: {:#?}", psbt); -//! let pj_params = payjoin::sender::Configuration::with_fee_contribution( -//! payjoin::bitcoin::Amount::from_sat(10000), -//! None, -//! ); //! ``` //! //! ### 3. (optional) Spawn a thread or async task that will broadcast the transaction after delay (e.g. 1 minute) unless canceled @@ -76,8 +69,10 @@ //! ### 4. Construct the request with the PSBT and parameters //! //! ``` -//! let (req, ctx) = link -//! .create_pj_request(psbt, pj_params) +//! let min_fee_rate = bitcoin::FeeRate::from_sat_per_vb(1); // SPECIFY YOUR USER'S MINIMUM FEE RATE +//! let (req, ctx) = RequestBuilder::from_psbt_and_uri(psbt, uri) +//! .with_context(|| "Failed to create payjoin request")? +//! .build_recommended(min_fee_rate) //! .with_context(|| "Failed to create payjoin request")?; //! ``` //! @@ -89,7 +84,6 @@ //! //! ``` //! let client = reqwest::blocking::Client::builder() -//! .danger_accept_invalid_certs(danger_accept_invalid_certs) //! .build() //! .with_context(|| "Failed to build reqwest http client")?; //! let response = client @@ -143,17 +137,18 @@ use std::str::FromStr; +use bitcoin::address::NetworkChecked; use bitcoin::psbt::Psbt; use bitcoin::{FeeRate, Script, ScriptBuf, Sequence, TxOut, Weight}; pub use error::{CreateRequestError, ValidationError}; pub(crate) use error::{InternalCreateRequestError, InternalValidationError}; use url::Url; -use self::error::ConfigurationError; use crate::input_type::InputType; use crate::psbt::PsbtExt; -use crate::send::error::InternalConfigurationError; +use crate::uri::UriExt; use crate::weight::{varint_size, ComputeWeight}; +use crate::PjUri; // See usize casts #[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))] @@ -163,30 +158,65 @@ mod error; type InternalResult = Result; -/// Builder for sender-side payjoin parameters -/// -/// These parameters define how client wants to handle Payjoin. -pub struct Configuration { +pub struct RequestBuilder<'a> { + psbt: Psbt, + uri: PjUri<'a>, disable_output_substitution: bool, fee_contribution: Option<(bitcoin::Amount, Option)>, clamp_fee_contribution: bool, min_fee_rate: FeeRate, } -impl Configuration { +impl<'a> RequestBuilder<'a> { + /// Prepare an HTTP request and request context to process the response + /// + /// An HTTP client will own the Request data while Context sticks around so + /// a `(Request, Context)` tuple is returned from `RequestBuilder::build()` + /// to keep them separated. + pub fn from_psbt_and_uri( + psbt: Psbt, + uri: crate::Uri<'a, NetworkChecked>, + ) -> Result { + let uri = uri + .check_pj_supported() + .map_err(|_| InternalCreateRequestError::UriDoesNotSupportPayjoin)?; + Ok(Self { + psbt, + uri, + // Sender's optional parameters + disable_output_substitution: false, + fee_contribution: None, + clamp_fee_contribution: false, + min_fee_rate: FeeRate::ZERO, + }) + } + + /// Disable output substitution even if the receiver didn't. + /// + /// This forbids receiver switching output or decreasing amount. + /// It is generally **not** recommended to set this as it may prevent the receiver from + /// doing advanced operations such as opening LN channels and it also guarantees the + /// receiver will **not** reward the sender with a discount. + pub fn always_disable_output_substitution(mut self, disable: bool) -> Self { + self.disable_output_substitution = disable; + self + } + // Calculate the recommended fee contribution for an Original PSBT. // // BIP 78 recommends contributing `originalPSBTFeeRate * vsize(sender_input_type)`. // The minfeerate parameter is set if the contribution is available in change. // // This method fails if no recommendation can be made or if the PSBT is malformed. - pub fn recommended( - psbt: &Psbt, - payout_scripts: impl IntoIterator, + pub fn build_recommended( + self, min_fee_rate: FeeRate, - ) -> Result { - let mut payout_scripts = payout_scripts.into_iter(); - if let Some((additional_fee_index, fee_available)) = psbt + ) -> Result<(Request, Context), CreateRequestError> { + // TODO support optional batched payout scripts. This would require a change to + // build() which now checks for a single payee. + let mut payout_scripts = std::iter::once(self.uri.address.script_pubkey()); + if let Some((additional_fee_index, fee_available)) = self + .psbt .unsigned_tx .output .clone() @@ -195,17 +225,18 @@ impl Configuration { .find(|(_, txo)| payout_scripts.all(|script| script != txo.script_pubkey)) .map(|(i, txo)| (i, bitcoin::Amount::from_sat(txo.value))) { - let input_types = psbt + let input_types = self + .psbt .input_pairs() .map(|input| { let txo = - input.previous_txout().map_err(InternalConfigurationError::PrevTxOut)?; + input.previous_txout().map_err(InternalCreateRequestError::PrevTxOut)?; Ok(InputType::from_spent_input(txo, input.psbtin) - .map_err(InternalConfigurationError::InputType)?) + .map_err(InternalCreateRequestError::InputType)?) }) - .collect::, ConfigurationError>>()?; + .collect::, InternalCreateRequestError>>()?; - let first_type = input_types.first().ok_or(InternalConfigurationError::NoInputs)?; + let first_type = input_types.first().ok_or(InternalCreateRequestError::NoInputs)?; // use cheapest default if mixed input types let mut input_vsize = InputType::Taproot.expected_input_weight(); // Check if all inputs are the same type @@ -216,20 +247,21 @@ impl Configuration { let recommended_additional_fee = min_fee_rate * input_vsize; if fee_available < recommended_additional_fee { log::warn!("Insufficient funds to maintain specified minimum feerate."); - return Ok(Configuration::with_fee_contribution( + return self.build_with_additional_fee( fee_available, Some(additional_fee_index), - ) - .clamp_fee_contribution(true)); + min_fee_rate, + true, + ); } - return Ok(Configuration::with_fee_contribution( + return self.build_with_additional_fee( recommended_additional_fee, Some(additional_fee_index), - ) - .clamp_fee_contribution(false) - .min_fee_rate(min_fee_rate)); + min_fee_rate, + false, + ); } - Ok(Configuration::non_incentivizing()) + self.build_non_incentivizing() } /// Offer the receiver contribution to pay for his input. @@ -239,56 +271,81 @@ impl Configuration { /// /// `change_index` specifies which output can be used to pay fee. If `None` is provided, then /// the output is auto-detected unless the supplied transaction has more than two outputs. - pub fn with_fee_contribution( + /// + /// `clamp_fee_contribution` decreases fee contribution instead of erroring. + /// + /// If this option is true and a transaction with change amount lower than fee + /// contribution is provided then instead of returning error the fee contribution will + /// be just lowered in the request to match the change amount. + pub fn build_with_additional_fee( + mut self, max_fee_contribution: bitcoin::Amount, change_index: Option, - ) -> Self { - Configuration { - disable_output_substitution: false, - fee_contribution: Some((max_fee_contribution, change_index)), - clamp_fee_contribution: false, - min_fee_rate: FeeRate::ZERO, - } + min_fee_rate: FeeRate, + clamp_fee_contribution: bool, + ) -> Result<(Request, Context), CreateRequestError> { + self.fee_contribution = Some((max_fee_contribution, change_index)); + self.clamp_fee_contribution = clamp_fee_contribution; + self.min_fee_rate = min_fee_rate; + self.build() } /// Perform Payjoin without incentivizing the payee to cooperate. /// /// While it's generally better to offer some contribution some users may wish not to. /// This function disables contribution. - pub fn non_incentivizing() -> Self { - Configuration { - disable_output_substitution: false, - fee_contribution: None, - clamp_fee_contribution: false, - min_fee_rate: FeeRate::ZERO, - } - } - - /// Disable output substitution even if the receiver didn't. - /// - /// This forbids receiver switching output or decreasing amount. - /// It is generally **not** recommended to set this as it may prevent the receiver from - /// doing advanced operations such as opening LN channels and it also guarantees the - /// receiver will **not** reward the sender with a discount. - pub fn always_disable_output_substitution(mut self, disable: bool) -> Self { - self.disable_output_substitution = disable; - self - } - - /// Decrease fee contribution instead of erroring. - /// - /// If this option is set and a transaction with change amount lower than fee - /// contribution is provided then instead of returning error the fee contribution will - /// be just lowered to match the change amount. - pub fn clamp_fee_contribution(mut self, clamp: bool) -> Self { - self.clamp_fee_contribution = clamp; - self + pub fn build_non_incentivizing(mut self) -> Result<(Request, Context), CreateRequestError> { + // since this is a builder, these should already be cleared + // but we'll reset them to be sure + self.fee_contribution = None; + self.clamp_fee_contribution = false; + self.min_fee_rate = FeeRate::ZERO; + self.build() } - /// Sets minimum fee rate required by the sender. - pub fn min_fee_rate(mut self, fee_rate: FeeRate) -> Self { - self.min_fee_rate = fee_rate; - self + fn build(self) -> Result<(Request, Context), CreateRequestError> { + let mut psbt = + self.psbt.validate().map_err(InternalCreateRequestError::InconsistentOriginalPsbt)?; + psbt.validate_input_utxos(true) + .map_err(InternalCreateRequestError::InvalidOriginalInput)?; + let disable_output_substitution = + self.uri.extras.disable_output_substitution || self.disable_output_substitution; + let payee = self.uri.address.script_pubkey(); + + check_single_payee(&psbt, &payee, self.uri.amount)?; + let fee_contribution = determine_fee_contribution( + &psbt, + &payee, + self.fee_contribution, + self.clamp_fee_contribution, + )?; + clear_unneeded_fields(&mut psbt); + + let zeroth_input = psbt.input_pairs().next().ok_or(InternalCreateRequestError::NoInputs)?; + + let sequence = zeroth_input.txin.sequence; + let txout = zeroth_input.previous_txout().expect("We already checked this above"); + let input_type = InputType::from_spent_input(txout, zeroth_input.psbtin).unwrap(); + let url = serialize_url( + self.uri.extras._endpoint.into(), + disable_output_substitution, + fee_contribution, + self.min_fee_rate, + ) + .map_err(InternalCreateRequestError::Url)?; + let body = serialize_psbt(&psbt); + Ok(( + Request { url, body }, + Context { + original_psbt: psbt, + disable_output_substitution, + fee_contribution, + payee, + input_type, + sequence, + min_fee_rate: self.min_fee_rate, + }, + )) } } @@ -312,8 +369,8 @@ pub struct Request { /// Data required for validation of response. /// -/// This type is used to process the response. It is returned from [`PjUriExt::create_pj_request()`](crate::PjUriExt::create_pj_request()) method -/// and you only need to call [`.process_response()`](crate::send::Context::process_response()) on it to continue BIP78 flow. +/// This type is used to process the response. Get it from [`RequestBuilder`](crate::send::RequestBuilder)'s build methods. +/// Then you only need to call [`.process_response()`](crate::send::Context::process_response()) on it to continue BIP78 flow. pub struct Context { original_psbt: Psbt, disable_output_substitution: bool, @@ -700,12 +757,13 @@ fn check_change_index( fn determine_fee_contribution( psbt: &Psbt, payee: &Script, - params: &Configuration, + fee_contribution: Option<(bitcoin::Amount, Option)>, + clamp_fee_contribution: bool, ) -> Result, InternalCreateRequestError> { - Ok(match params.fee_contribution { - Some((fee, None)) => find_change_index(psbt, payee, fee, params.clamp_fee_contribution)?, + Ok(match fee_contribution { + Some((fee, None)) => find_change_index(psbt, payee, fee, clamp_fee_contribution)?, Some((fee, Some(index))) => - Some(check_change_index(psbt, payee, fee, index, params.clamp_fee_contribution)?), + Some(check_change_index(psbt, payee, fee, index, clamp_fee_contribution)?), None => None, }) } @@ -739,47 +797,6 @@ fn serialize_psbt(psbt: &Psbt) -> Vec { bitcoin::base64::encode(bytes).into_bytes() } -pub(crate) fn from_psbt_and_uri( - mut psbt: Psbt, - uri: crate::uri::PjUri<'_>, - params: Configuration, -) -> Result<(Request, Context), CreateRequestError> { - psbt.validate_input_utxos(true).map_err(InternalCreateRequestError::InvalidOriginalInput)?; - let disable_output_substitution = - uri.extras.disable_output_substitution || params.disable_output_substitution; - let payee = uri.address.script_pubkey(); - - check_single_payee(&psbt, &payee, uri.amount)?; - let fee_contribution = determine_fee_contribution(&psbt, &payee, ¶ms)?; - clear_unneeded_fields(&mut psbt); - - let zeroth_input = psbt.input_pairs().next().ok_or(InternalCreateRequestError::NoInputs)?; - - let sequence = zeroth_input.txin.sequence; - let txout = zeroth_input.previous_txout().expect("We already checked this above"); - let input_type = InputType::from_spent_input(txout, zeroth_input.psbtin).unwrap(); - let url = serialize_url( - uri.extras._endpoint.into(), - disable_output_substitution, - fee_contribution, - params.min_fee_rate, - ) - .map_err(InternalCreateRequestError::Url)?; - let body = serialize_psbt(&psbt); - Ok(( - Request { url, body }, - Context { - original_psbt: psbt, - disable_output_substitution, - fee_contribution, - payee, - input_type, - sequence, - min_fee_rate: params.min_fee_rate, - }, - )) -} - #[cfg(test)] mod tests { #[test] diff --git a/payjoin/src/uri.rs b/payjoin/src/uri.rs index bd8f1426d..b8f32b37c 100644 --- a/payjoin/src/uri.rs +++ b/payjoin/src/uri.rs @@ -5,9 +5,6 @@ use bitcoin::address::{Error, NetworkChecked, NetworkUnchecked}; use bitcoin::Network; use url::Url; -#[cfg(feature = "send")] -use crate::send; - #[derive(Debug, Clone)] pub enum Payjoin { Supported(PayjoinParams), @@ -50,21 +47,6 @@ pub trait UriExtNetworkUnchecked<'a>: sealed::UriExtNetworkUnchecked { fn assume_checked(self) -> Uri<'a, NetworkChecked>; } -pub trait PjUriExt: sealed::UriExt { - /// Prepare an HTTP request and request context to process the response - /// - /// An HTTP client will own the Request data while Context sticks around so - /// a `(Request, Context)` tuple is returned to keep them separated. call: - /// - /// `let (request, context) = uri.create_pj_request(psbt, params);` - #[cfg(feature = "send")] - fn create_pj_request( - self, - psbt: bitcoin::psbt::Psbt, - params: send::Configuration, - ) -> Result<(send::Request, send::Context), send::CreateRequestError>; -} - pub trait UriExt<'a>: sealed::UriExt { fn check_pj_supported(self) -> Result, bip21::Uri<'a>>; } @@ -89,24 +71,8 @@ impl<'a> UriExtNetworkUnchecked<'a> for Uri<'a, NetworkUnchecked> { } } -impl<'a> PjUriExt for PjUri<'a> { - #[cfg(feature = "send")] - fn create_pj_request( - self, - psbt: bitcoin::psbt::Psbt, - params: send::Configuration, - ) -> Result<(send::Request, send::Context), send::CreateRequestError> { - use crate::psbt::PsbtExt; - - let valid_psbt = - psbt.validate().map_err(send::InternalCreateRequestError::InconsistentOriginalPsbt)?; - send::from_psbt_and_uri(valid_psbt, self, params) - } -} - impl<'a> UriExt<'a> for Uri<'a, NetworkChecked> { fn check_pj_supported(self) -> Result, bip21::Uri<'a>> { - //let checked_address = self.address.assume_checked(); match self.extras { Payjoin::Supported(payjoin) => { let mut uri = bip21::Uri::with_extras(self.address, payjoin); diff --git a/payjoin/tests/integration.rs b/payjoin/tests/integration.rs index c99c05711..dd4cc545b 100644 --- a/payjoin/tests/integration.rs +++ b/payjoin/tests/integration.rs @@ -11,8 +11,8 @@ mod integration { use log::{debug, log_enabled, Level}; use payjoin::bitcoin::base64; use payjoin::receive::Headers; - use payjoin::send::Request; - use payjoin::{bitcoin, Error, PjUriExt, Uri, UriExt}; + use payjoin::send::{Request, RequestBuilder}; + use payjoin::{bitcoin, Error, Uri}; #[test] fn integration_test() { @@ -53,9 +53,8 @@ mod integration { pj_receiver_address.to_qr_uri(), amount.to_btc() ); - let pj_uri = Uri::from_str(&pj_uri_string).unwrap().assume_checked(); - let pj_uri = pj_uri.check_pj_supported().expect("Bad Uri"); - + 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()); @@ -78,11 +77,15 @@ 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 pj_params = payjoin::send::Configuration::with_fee_contribution( - payjoin::bitcoin::Amount::from_sat(10000), - None, - ); - let (req, ctx) = pj_uri.create_pj_request(psbt, pj_params).unwrap(); + let (req, ctx) = RequestBuilder::from_psbt_and_uri(psbt, pj_uri) + .unwrap() + .build_with_additional_fee( + payjoin::bitcoin::Amount::from_sat(10000), + None, + bitcoin::FeeRate::ZERO, + false, + ) + .unwrap(); let headers = HeaderMock::from_vec(&req.body); // **********************