Skip to content

Commit d81d8ae

Browse files
Force v1 receiver to also use a mutable reference to the state closures
By also using a mutable reference to the closure on our v1 receiver instead of taking ownership at this level we can keep the code consistent across versions.
1 parent 4dd746a commit d81d8ae

File tree

5 files changed

+36
-38
lines changed

5 files changed

+36
-38
lines changed

payjoin-cli/src/app/v1.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -321,17 +321,18 @@ impl App {
321321
let _to_broadcast_in_failure_case = proposal.extract_tx_to_schedule_broadcast();
322322

323323
// Receive Check 2: receiver can't sign for proposal inputs
324-
let proposal = proposal.check_inputs_not_owned(|input| {
324+
let proposal = proposal.check_inputs_not_owned(&mut |input| {
325325
wallet.is_mine(input).map_err(|e| ImplementationError::from(e.into_boxed_dyn_error()))
326326
})?;
327327
log::trace!("check2");
328328

329329
// Receive Check 3: have we seen this input before? More of a check for non-interactive i.e. payment processor receivers.
330-
let payjoin = proposal
331-
.check_no_inputs_seen_before(|input| Ok(self.db.insert_input_seen_before(*input)?))?;
330+
let payjoin = proposal.check_no_inputs_seen_before(&mut |input| {
331+
Ok(self.db.insert_input_seen_before(*input)?)
332+
})?;
332333
log::trace!("check3");
333334

334-
let payjoin = payjoin.identify_receiver_outputs(|output_script| {
335+
let payjoin = payjoin.identify_receiver_outputs(&mut |output_script| {
335336
wallet
336337
.is_mine(output_script)
337338
.map_err(|e| ImplementationError::from(e.into_boxed_dyn_error()))

payjoin/src/core/receive/multiparty/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ pub struct MaybeInputsOwned {
112112
impl MaybeInputsOwned {
113113
pub fn check_inputs_not_owned(
114114
self,
115-
is_owned: impl Fn(&bitcoin::Script) -> Result<bool, ImplementationError>,
115+
is_owned: &mut impl FnMut(&bitcoin::Script) -> Result<bool, ImplementationError>,
116116
) -> Result<MaybeInputsSeen, Error> {
117117
let inner = self.v1.check_inputs_not_owned(is_owned)?;
118118
Ok(MaybeInputsSeen { v1: inner, contexts: self.contexts })
@@ -127,7 +127,7 @@ pub struct MaybeInputsSeen {
127127
impl MaybeInputsSeen {
128128
pub fn check_no_inputs_seen_before(
129129
self,
130-
is_seen: impl Fn(&bitcoin::OutPoint) -> Result<bool, ImplementationError>,
130+
is_seen: &mut impl FnMut(&bitcoin::OutPoint) -> Result<bool, ImplementationError>,
131131
) -> Result<OutputsUnknown, Error> {
132132
let inner = self.v1.check_no_inputs_seen_before(is_seen)?;
133133
Ok(OutputsUnknown { v1: inner, contexts: self.contexts })
@@ -142,7 +142,7 @@ pub struct OutputsUnknown {
142142
impl OutputsUnknown {
143143
pub fn identify_receiver_outputs(
144144
self,
145-
is_receiver_output: impl Fn(&bitcoin::Script) -> Result<bool, ImplementationError>,
145+
is_receiver_output: &mut impl FnMut(&bitcoin::Script) -> Result<bool, ImplementationError>,
146146
) -> Result<WantsOutputs, Error> {
147147
let inner = self.v1.identify_receiver_outputs(is_receiver_output)?;
148148
Ok(WantsOutputs { v1: inner, contexts: self.contexts })

payjoin/src/core/receive/v1/mod.rs

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,8 @@ impl MaybeInputsOwned {
160160
/// An attacker can try to spend the receiver's own inputs. This check prevents that.
161161
pub fn check_inputs_not_owned(
162162
self,
163-
is_owned: impl FnMut(&Script) -> Result<bool, ImplementationError>,
163+
is_owned: &mut impl FnMut(&Script) -> Result<bool, ImplementationError>,
164164
) -> Result<MaybeInputsSeen, ReplyableError> {
165-
let mut is_owned = is_owned;
166165
let mut err: Result<(), ReplyableError> = Ok(());
167166
if let Some(e) = self
168167
.psbt
@@ -207,9 +206,8 @@ impl MaybeInputsSeen {
207206
/// original proposal PSBT of the current, new payjoin.
208207
pub fn check_no_inputs_seen_before(
209208
self,
210-
is_known: impl FnMut(&OutPoint) -> Result<bool, ImplementationError>,
209+
is_known: &mut impl FnMut(&OutPoint) -> Result<bool, ImplementationError>,
211210
) -> Result<OutputsUnknown, ReplyableError> {
212-
let mut is_known = is_known;
213211
self.psbt.input_pairs().try_for_each(|input| {
214212
match is_known(&input.txin.previous_output) {
215213
Ok(false) => Ok::<(), ReplyableError>(()),
@@ -250,9 +248,8 @@ impl OutputsUnknown {
250248
/// outputs.
251249
pub fn identify_receiver_outputs(
252250
self,
253-
is_receiver_output: impl FnMut(&Script) -> Result<bool, ImplementationError>,
251+
is_receiver_output: &mut impl FnMut(&Script) -> Result<bool, ImplementationError>,
254252
) -> Result<WantsOutputs, ReplyableError> {
255-
let mut is_receiver_output = is_receiver_output;
256253
let owned_vouts: Vec<usize> = self
257254
.psbt
258255
.unsigned_tx
@@ -912,11 +909,11 @@ pub(crate) mod test {
912909
fn wants_outputs_from_test_vector(proposal: UncheckedProposal) -> WantsOutputs {
913910
proposal
914911
.assume_interactive_receiver()
915-
.check_inputs_not_owned(|_| Ok(false))
912+
.check_inputs_not_owned(&mut |_| Ok(false))
916913
.expect("No inputs should be owned")
917-
.check_no_inputs_seen_before(|_| Ok(false))
914+
.check_no_inputs_seen_before(&mut |_| Ok(false))
918915
.expect("No inputs should be seen before")
919-
.identify_receiver_outputs(|script| {
916+
.identify_receiver_outputs(&mut |script| {
920917
let network = Network::Bitcoin;
921918
Ok(Address::from_script(script, network).unwrap()
922919
== Address::from_str("3CZZi7aWFugaCdUCS15dgrUUViupmB8bVM")
@@ -945,20 +942,20 @@ pub(crate) mod test {
945942
Ok(ret)
946943
}
947944

948-
let maybe_inputs_seen =
949-
maybe_inputs_owned.check_inputs_not_owned(|_| mock_callback(&mut call_count, false));
945+
let maybe_inputs_seen = maybe_inputs_owned
946+
.check_inputs_not_owned(&mut |_| mock_callback(&mut call_count, false));
950947
assert_eq!(call_count, 1);
951948

952949
let outputs_unknown = maybe_inputs_seen
953950
.map_err(|_| "Check inputs owned closure failed".to_string())
954951
.expect("Next receiver state should be accessible")
955-
.check_no_inputs_seen_before(|_| mock_callback(&mut call_count, false));
952+
.check_no_inputs_seen_before(&mut |_| mock_callback(&mut call_count, false));
956953
assert_eq!(call_count, 2);
957954

958955
let _wants_outputs = outputs_unknown
959956
.map_err(|_| "Check no inputs seen closure failed".to_string())
960957
.expect("Next receiver state should be accessible")
961-
.identify_receiver_outputs(|_| mock_callback(&mut call_count, true));
958+
.identify_receiver_outputs(&mut |_| mock_callback(&mut call_count, true));
962959
// there are 2 receiver outputs so we should expect this callback to run twice incrementing
963960
// call count twice
964961
assert_eq!(call_count, 4);
@@ -1014,11 +1011,11 @@ pub(crate) mod test {
10141011
let proposal = unchecked_proposal_from_test_vector();
10151012
let wants_inputs = proposal
10161013
.assume_interactive_receiver()
1017-
.check_inputs_not_owned(|_| Ok(false))
1014+
.check_inputs_not_owned(&mut |_| Ok(false))
10181015
.expect("No inputs should be owned")
1019-
.check_no_inputs_seen_before(|_| Ok(false))
1016+
.check_no_inputs_seen_before(&mut |_| Ok(false))
10201017
.expect("No inputs should be seen before")
1021-
.identify_receiver_outputs(|script| {
1018+
.identify_receiver_outputs(&mut |script| {
10221019
let network = Network::Bitcoin;
10231020
let target_address = Address::from_str("3CZZi7aWFugaCdUCS15dgrUUViupmB8bVM")
10241021
.map_err(ImplementationError::new)?

payjoin/src/core/receive/v2/session.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -178,15 +178,15 @@ mod tests {
178178
let maybe_inputs_owned = unchecked_proposal.clone().assume_interactive_receiver();
179179
let maybe_inputs_seen = maybe_inputs_owned
180180
.clone()
181-
.check_inputs_not_owned(|_| Ok(false))
181+
.check_inputs_not_owned(&mut |_| Ok(false))
182182
.expect("No inputs should be owned");
183183
let outputs_unknown = maybe_inputs_seen
184184
.clone()
185-
.check_no_inputs_seen_before(|_| Ok(false))
185+
.check_no_inputs_seen_before(&mut |_| Ok(false))
186186
.expect("No inputs should be seen before");
187187
let wants_outputs = outputs_unknown
188188
.clone()
189-
.identify_receiver_outputs(|_| Ok(true))
189+
.identify_receiver_outputs(&mut |_| Ok(true))
190190
.expect("Outputs should be identified");
191191
let wants_inputs = wants_outputs.clone().commit_outputs();
192192
let wants_fee_range = wants_inputs.clone().commit_inputs();
@@ -375,15 +375,15 @@ mod tests {
375375
let maybe_inputs_owned = unchecked_proposal.clone().assume_interactive_receiver();
376376
let maybe_inputs_seen = maybe_inputs_owned
377377
.clone()
378-
.check_inputs_not_owned(|_| Ok(false))
378+
.check_inputs_not_owned(&mut |_| Ok(false))
379379
.expect("No inputs should be owned");
380380
let outputs_unknown = maybe_inputs_seen
381381
.clone()
382-
.check_no_inputs_seen_before(|_| Ok(false))
382+
.check_no_inputs_seen_before(&mut |_| Ok(false))
383383
.expect("No inputs should be seen before");
384384
let wants_outputs = outputs_unknown
385385
.clone()
386-
.identify_receiver_outputs(|_| Ok(true))
386+
.identify_receiver_outputs(&mut |_| Ok(true))
387387
.expect("Outputs should be identified");
388388
let wants_inputs = wants_outputs.clone().commit_outputs();
389389
let wants_fee_range = wants_inputs.clone().commit_inputs();
@@ -425,15 +425,15 @@ mod tests {
425425
let maybe_inputs_owned = unchecked_proposal.clone().assume_interactive_receiver();
426426
let maybe_inputs_seen = maybe_inputs_owned
427427
.clone()
428-
.check_inputs_not_owned(|_| Ok(false))
428+
.check_inputs_not_owned(&mut |_| Ok(false))
429429
.expect("No inputs should be owned");
430430
let outputs_unknown = maybe_inputs_seen
431431
.clone()
432-
.check_no_inputs_seen_before(|_| Ok(false))
432+
.check_no_inputs_seen_before(&mut |_| Ok(false))
433433
.expect("No inputs should be seen before");
434434
let wants_outputs = outputs_unknown
435435
.clone()
436-
.identify_receiver_outputs(|_| Ok(true))
436+
.identify_receiver_outputs(&mut |_| Ok(true))
437437
.expect("Outputs should be identified");
438438
let wants_inputs = wants_outputs.clone().commit_outputs();
439439
let wants_fee_range = wants_inputs.clone().commit_inputs();

payjoin/tests/integration.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1084,15 +1084,15 @@ mod integration {
10841084
// In a ns1r payment, the merged psbt is not broadcastable
10851085
let proposal =
10861086
multiparty_proposal.check_broadcast_suitability(None, |_| Ok(true))?;
1087-
let proposal = proposal.check_inputs_not_owned(|input| {
1087+
let proposal = proposal.check_inputs_not_owned(&mut |input| {
10881088
let address =
10891089
bitcoin::Address::from_script(input, bitcoin::Network::Regtest).unwrap();
10901090
Ok(receiver.get_address_info(&address).unwrap().is_mine.unwrap())
10911091
})?;
10921092

10931093
let payjoin = proposal
1094-
.check_no_inputs_seen_before(|_| Ok(false))?
1095-
.identify_receiver_outputs(|output_script| {
1094+
.check_no_inputs_seen_before(&mut |_| Ok(false))?
1095+
.identify_receiver_outputs(&mut |output_script| {
10961096
let address =
10971097
bitcoin::Address::from_script(output_script, bitcoin::Network::Regtest)
10981098
.unwrap();
@@ -1401,7 +1401,7 @@ mod integration {
14011401
let _to_broadcast_in_failure_case = proposal.extract_tx_to_schedule_broadcast();
14021402

14031403
// Receive Check 2: receiver can't sign for proposal inputs
1404-
let proposal = proposal.check_inputs_not_owned(|input| {
1404+
let proposal = proposal.check_inputs_not_owned(&mut |input| {
14051405
let address = bitcoin::Address::from_script(input, bitcoin::Network::Regtest)
14061406
.map_err(ImplementationError::new)?;
14071407
receiver
@@ -1412,8 +1412,8 @@ mod integration {
14121412

14131413
// Receive Check 3: have we seen this input before? More of a check for non-interactive i.e. payment processor receivers.
14141414
let payjoin = proposal
1415-
.check_no_inputs_seen_before(|_| Ok(false))?
1416-
.identify_receiver_outputs(|output_script| {
1415+
.check_no_inputs_seen_before(&mut |_| Ok(false))?
1416+
.identify_receiver_outputs(&mut |output_script| {
14171417
let address =
14181418
bitcoin::Address::from_script(output_script, bitcoin::Network::Regtest)
14191419
.map_err(ImplementationError::new)?;

0 commit comments

Comments
 (0)