diff --git a/payjoin/src/send/mod.rs b/payjoin/src/send/mod.rs index ea54f585..36dd08f5 100644 --- a/payjoin/src/send/mod.rs +++ b/payjoin/src/send/mod.rs @@ -37,12 +37,19 @@ pub mod v2; type InternalResult = Result; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "v2", derive(serde::Serialize, serde::Deserialize))] +pub(crate) struct AdditionalFeeContribution { + max_amount: Amount, + vout: usize, +} + /// Data required to validate the response against the original PSBT. #[derive(Debug, Clone)] pub struct PsbtContext { original_psbt: Psbt, disable_output_substitution: bool, - fee_contribution: Option<(bitcoin::Amount, usize)>, + fee_contribution: Option, min_fee_rate: FeeRate, payee: ScriptBuf, } @@ -224,7 +231,10 @@ impl PsbtContext { // fee output ( Some((original_output_index, original_output)), - Some((max_fee_contrib, fee_contrib_idx)), + Some(AdditionalFeeContribution { + max_amount: max_fee_contrib, + vout: fee_contrib_idx, + }), ) if proposed_txout.script_pubkey == original_output.script_pubkey && *original_output_index == fee_contrib_idx => { @@ -338,7 +348,7 @@ fn find_change_index( payee: &Script, fee: bitcoin::Amount, clamp_fee_contribution: bool, -) -> Result, InternalBuildSenderError> { +) -> Result, InternalBuildSenderError> { match (psbt.unsigned_tx.output.len(), clamp_fee_contribution) { (0, _) => return Err(InternalBuildSenderError::NoOutputs), (1, false) if psbt.unsigned_tx.output[0].script_pubkey == *payee => @@ -356,7 +366,10 @@ fn find_change_index( .find(|(_, output)| output.script_pubkey != *payee) .ok_or(InternalBuildSenderError::MultiplePayeeOutputs)?; - Ok(Some((check_fee_output_amount(output, fee, clamp_fee_contribution)?, index))) + Ok(Some(AdditionalFeeContribution { + max_amount: check_fee_output_amount(output, fee, clamp_fee_contribution)?, + vout: index, + })) } /// Check that the change output index is not out of bounds @@ -367,7 +380,7 @@ fn check_change_index( fee: bitcoin::Amount, index: usize, clamp_fee_contribution: bool, -) -> Result<(bitcoin::Amount, usize), InternalBuildSenderError> { +) -> Result { let output = psbt .unsigned_tx .output @@ -376,7 +389,10 @@ fn check_change_index( if output.script_pubkey == *payee { return Err(InternalBuildSenderError::ChangeIndexPointsAtPayee); } - Ok((check_fee_output_amount(output, fee, clamp_fee_contribution)?, index)) + Ok(AdditionalFeeContribution { + max_amount: check_fee_output_amount(output, fee, clamp_fee_contribution)?, + vout: index, + }) } fn determine_fee_contribution( @@ -384,7 +400,7 @@ fn determine_fee_contribution( payee: &Script, fee_contribution: Option<(bitcoin::Amount, Option)>, clamp_fee_contribution: bool, -) -> Result, InternalBuildSenderError> { +) -> Result, InternalBuildSenderError> { Ok(match fee_contribution { Some((fee, None)) => find_change_index(psbt, payee, fee, clamp_fee_contribution)?, Some((fee, Some(index))) => @@ -396,7 +412,7 @@ fn determine_fee_contribution( fn serialize_url( endpoint: Url, disable_output_substitution: bool, - fee_contribution: Option<(bitcoin::Amount, usize)>, + fee_contribution: Option, min_fee_rate: FeeRate, version: &str, ) -> Result { @@ -405,10 +421,10 @@ fn serialize_url( if disable_output_substitution { url.query_pairs_mut().append_pair("disableoutputsubstitution", "true"); } - if let Some((amount, index)) = fee_contribution { + if let Some(AdditionalFeeContribution { max_amount, vout }) = fee_contribution { url.query_pairs_mut() - .append_pair("additionalfeeoutputindex", &index.to_string()) - .append_pair("maxadditionalfeecontribution", &amount.to_sat().to_string()); + .append_pair("additionalfeeoutputindex", &vout.to_string()) + .append_pair("maxadditionalfeecontribution", &max_amount.to_sat().to_string()); } if min_fee_rate > FeeRate::ZERO { // TODO serialize in rust-bitcoin @@ -428,6 +444,7 @@ pub(crate) mod test { use super::serialize_url; use crate::psbt::PsbtExt; + use crate::send::AdditionalFeeContribution; pub(crate) const ORIGINAL_PSBT: &str = "cHNidP8BAHMCAAAAAY8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////AtyVuAUAAAAAF6kUHehJ8GnSdBUOOv6ujXLrWmsJRDCHgIQeAAAAAAAXqRR3QJbbz0hnQ8IvQ0fptGn+votneofTAAAAAAEBIKgb1wUAAAAAF6kU3k4ekGHKWRNbA1rV5tR5kEVDVNCHAQcXFgAUx4pFclNVgo1WWAdN1SYNX8tphTABCGsCRzBEAiB8Q+A6dep+Rz92vhy26lT0AjZn4PRLi8Bf9qoB/CMk0wIgP/Rj2PWZ3gEjUkTlhDRNAQ0gXwTO7t9n+V14pZ6oljUBIQMVmsAaoNWHVMS02LfTSe0e388LNitPa1UQZyOihY+FFgABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUAAA="; const PAYJOIN_PROPOSAL: &str = "cHNidP8BAJwCAAAAAo8nutGgJdyYGXWiBEb45Hoe9lWGbkxh/6bNiOJdCDuDAAAAAAD+////jye60aAl3JgZdaIERvjkeh72VYZuTGH/ps2I4l0IO4MBAAAAAP7///8CJpW4BQAAAAAXqRQd6EnwadJ0FQ46/q6NcutaawlEMIcACT0AAAAAABepFHdAltvPSGdDwi9DR+m0af6+i2d6h9MAAAAAAQEgqBvXBQAAAAAXqRTeTh6QYcpZE1sDWtXm1HmQRUNU0IcBBBYAFMeKRXJTVYKNVlgHTdUmDV/LaYUwIgYDFZrAGqDVh1TEtNi300ntHt/PCzYrT2tVEGcjooWPhRYYSFzWUDEAAIABAACAAAAAgAEAAAAAAAAAAAEBIICEHgAAAAAAF6kUyPLL+cphRyyI5GTUazV0hF2R2NWHAQcXFgAUX4BmVeWSTJIEwtUb5TlPS/ntohABCGsCRzBEAiBnu3tA3yWlT0WBClsXXS9j69Bt+waCs9JcjWtNjtv7VgIge2VYAaBeLPDB6HGFlpqOENXMldsJezF9Gs5amvDQRDQBIQJl1jz1tBt8hNx2owTm+4Du4isx0pmdKNMNIjjaMHFfrQABABYAFEb2Giu6c4KO5YW0pfw3lGp9jMUUIgICygvBWB5prpfx61y1HDAwo37kYP3YRJBvAjtunBAur3wYSFzWUDEAAIABAACAAAAAgAEAAAABAAAAAAA="; @@ -439,7 +456,10 @@ pub(crate) mod test { super::PsbtContext { original_psbt, disable_output_substitution: false, - fee_contribution: Some((bitcoin::Amount::from_sat(182), 0)), + fee_contribution: Some(AdditionalFeeContribution { + max_amount: bitcoin::Amount::from_sat(182), + vout: 0, + }), min_fee_rate: FeeRate::ZERO, payee, } diff --git a/payjoin/src/send/v1.rs b/payjoin/src/send/v1.rs index be5b9e7c..0171edf1 100644 --- a/payjoin/src/send/v1.rs +++ b/payjoin/src/send/v1.rs @@ -219,7 +219,7 @@ pub struct Sender { /// Disallow reciever to substitute original outputs. pub(crate) disable_output_substitution: bool, /// (maxadditionalfeecontribution, additionalfeeoutputindex) - pub(crate) fee_contribution: Option<(bitcoin::Amount, usize)>, + pub(crate) fee_contribution: Option, pub(crate) min_fee_rate: FeeRate, /// Script of the person being paid pub(crate) payee: ScriptBuf, diff --git a/payjoin/src/send/v2/mod.rs b/payjoin/src/send/v2/mod.rs index 90f8938d..82ace14a 100644 --- a/payjoin/src/send/v2/mod.rs +++ b/payjoin/src/send/v2/mod.rs @@ -196,7 +196,7 @@ impl Sender { fn serialize_v2_body( psbt: &Psbt, disable_output_substitution: bool, - fee_contribution: Option<(bitcoin::Amount, usize)>, + fee_contribution: Option, min_fee_rate: FeeRate, ) -> Result, CreateRequestError> { // Grug say localhost base be discarded anyway. no big brain needed.