Skip to content

Retry pj on version error#151

Closed
jbesraa wants to merge 2 commits into
payjoin:masterfrom
jbesraa:retry-pj-on-version-error
Closed

Retry pj on version error#151
jbesraa wants to merge 2 commits into
payjoin:masterfrom
jbesraa:retry-pj-on-version-error

Conversation

@jbesraa

@jbesraa jbesraa commented Dec 22, 2023

Copy link
Copy Markdown
Contributor

resolves #144
blocked by #120

@DanGould

Copy link
Copy Markdown
Member

No longer blocked

@jbesraa
jbesraa force-pushed the retry-pj-on-version-error branch from 93b0def to c309fd8 Compare January 28, 2024 17:24
@jbesraa jbesraa changed the title [WIP] Retry pj on version error Retry pj on version error Jan 28, 2024
@jbesraa

jbesraa commented Jan 29, 2024

Copy link
Copy Markdown
Contributor Author

currently blocked by #182 as we need to access the supported versions array to check if v1 is supported

also by #184

@DanGould

DanGould commented Jan 29, 2024

Copy link
Copy Markdown
Member

Rather than #182 I think we just want to expose WellKnownError in the API and then match against it:

            match ctx.process_response(&mut response.into_reader()) {
                Ok(Some(psbt)) => return Ok(psbt),
                Ok(None) => std::thread::sleep(std::time::Duration::from_secs(5)),
                Err(re) => {
                    println!("{}", re);
                    match re {
                        payjoin::send::ResponseError::WellKnown(k) => {
                            match k {
                                payjoin::send::WellKnownError::VersionUnsupported(_, v) => {
                                    if v.contains(&2) {
                                        todo!();
                                    }
                                }
                                _ => todo!()
                            }
                        }
                        _ => todo!(),
                    }
                    log::debug!("{:?}", re);
                }
            }

Note I don't recommend this level of nesting in place, it's merely an example. A function should probably take care of it.

@DanGould

Copy link
Copy Markdown
Member

See the comment here for how to resolve the clone issue. I recommend cloning in the exceptional case rather than within extract_v1 #183 (comment)

@jbesraa
jbesraa force-pushed the retry-pj-on-version-error branch 2 times, most recently from e937a34 to f15d8a3 Compare February 1, 2024 09:08
@jbesraa
jbesraa requested a review from DanGould February 1, 2024 09:11
@jbesraa
jbesraa marked this pull request as ready for review February 1, 2024 09:11
Comment thread payjoin-cli/src/app.rs Outdated
"Sending fallback v1 request to {} after v2 request attempt fail",
&req.url
);
let response = spawn_blocking(move || {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is doing the trick, I think we should probably create an http module already with the content_type set and just providereq , that will save some duplicated lines of code.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes I think payjoin-cli would benefit from hexagonal design separation of concerns. The whole crate is an imperative mess at the moment, especially with multiple features

Comment thread payjoin-cli/src/app.rs Outdated
Comment thread payjoin-cli/src/app.rs Outdated
@DanGould
DanGould marked this pull request as draft February 1, 2024 15:33

@DanGould DanGould left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK.

The most important change I'd like to see is handle_response_error(e) logic happening inside the match ctx_process_response block rather than let error = assignment followed by a block of handler logic.

Since you haven't said this has been tested yet I converted it to draft. Open seems appropriate once at least a v2 sender to v1 receiver has a successful version downgrade payjoin.

Comment thread payjoin/src/send/error.rs Outdated
Comment on lines +322 to +327
/// Whether the error provided by the payjoin receiver is suggesting a specific version of
/// payjoin.
///
/// This is useful for when you support multiple versions of payjoin. For example, if you
/// support v1 and v2(BiP78 && BiP77 respectively), you can check if the error is suggesting a
/// specific version and if it does, you can retry the request based on the asked version.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going to spend the time to document functions let's write them with complete sentences and conventional grammar.

Suggested change
/// Whether the error provided by the payjoin receiver is suggesting a specific version of
/// payjoin.
///
/// This is useful for when you support multiple versions of payjoin. For example, if you
/// support v1 and v2(BiP78 && BiP77 respectively), you can check if the error is suggesting a
/// specific version and if it does, you can retry the request based on the asked version.
/// Check whether or not the payjoin receiver supports the supplied version.
///
/// This is useful when you support multiple versions of payjoin. For example, if you
/// support v1 and v2 (BIP78 and BIP77 respectively) and sent a v2 request that
/// resulted in this error, you could pass `1` to test whether or not the receiver might
/// respond well to another request using the older version.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, changed.

Comment thread payjoin-cli/src/app.rs Outdated
continue;
}
}
Err(error) => error,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather than assign let error = and follow this block with logic, I suggest writing be a handle_response_error(e) helper which can be called here when Err(e) is matched to contain attempt_version_downgrade logic and then handle the ultimate error if all else fails.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed.
decided to go with attemp_v1 and guarded that for v2 feature as it felt more correct

@DanGould DanGould Feb 2, 2024

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with the concept of abstraction into attempt_v1 (check spelling) and wonder if it could be abstracted to serve both v1 and v2 features? Does it for sure need to be feature gated? Both v1 and v2 sender attempt v1 sends. I think it's more common to name fns try_ instead of attempt_ too.

Comment thread payjoin/src/send/error.rs Outdated
Comment thread payjoin/src/send/error.rs Outdated
@jbesraa
jbesraa force-pushed the retry-pj-on-version-error branch from f15d8a3 to ac24cea Compare February 2, 2024 13:15
@jbesraa

jbesraa commented Feb 2, 2024

Copy link
Copy Markdown
Contributor Author

Ok so I think I went through all the comments, but I am yet to have time to test it.
Probably will do that tomorrow/monday.

Comment thread payjoin-cli/src/app.rs Outdated
println!("Sent fallback v1 request");
ctx.process_response(&mut response.into_reader()).map_err(|e| {
log::debug!("Error processing response: {:?}", e);
anyhow!("Failed to process response {}", e)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we dont really handle the error here, but that might be too much error handling? what do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now on line 237 the error for v2 still gets printed if the retry happens. It seems logical to me to handle the error by retrying INSTEAD of returning, and then handle both v1 and v2 ctx.process_response errors with the same piece of code after if you still end up with an error. I think I'd just leave ctx.process_response(&mut response.into_reader()) to be returned here instead of printing. Do all the printing and merged error handling in long_poll_post

@jbesraa
jbesraa force-pushed the retry-pj-on-version-error branch 2 times, most recently from 9ecb1d9 to cf4bec5 Compare February 5, 2024 16:29
  Add `is_version_supported` for `ResponseError`
  public API so users could check if the error
  is suggesting to send a payjoin request
  using different version.
  When we make a payjoin request based on version 2 and the payjoin
  receiver returns an error indicating they only support version 1, we
  attempt to make a version 1 request.
@jbesraa
jbesraa force-pushed the retry-pj-on-version-error branch from cf4bec5 to 61a721b Compare February 5, 2024 17:47
Comment thread payjoin-cli/src/app.rs
}

#[cfg(feature = "v2")]
async fn try_v1(&self, req_ctx: &mut payjoin::send::RequestContext) -> Result<Psbt> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this file both sends and receives so this fn should probably be called try_send_v1

Comment thread payjoin-cli/src/app.rs
Comment on lines +236 to +239
return self.try_v1(req_ctx).await;
};
println!("{}", error);
log::debug!("{:?}", error);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

returning early on line 236 here will skip printing the error if one is encountered. Rather, if an error is encountered there it should run on the same log statements. This will also require try_v1 returns Result<Psbt, ResponseError> instead of an anyhow::Error too. Then the last line of try_v1 can just be the process function and does not need to be wrapped in Ok()

Err(mut error) => {
    if error.is_version_supported(&1) {
        match self.try_v1(req_ctx).await {
            Ok(res) => return Ok(res),
            Err(e) => error = e,
        }
    };
    println!("{}", error);
    log::debug!("{:?}", error);
}

@DanGould

DanGould commented Feb 8, 2024

Copy link
Copy Markdown
Member

In the PDK Contributors Sync today we discussed the possible downgrade paths. This PR describes a downgrade where a v2 sender tries to send a v2 request to a v1 receiver who responds that they only accept v1 requests, however, is it even possible for a v2 sender to make a v2 request to a v1 receiver without a v2 ohttp= param? I suppose it's possible that a payload with a v=2 param is passed to the v1 receiver, but that would be an oddity. @jbesraa How do you think about handling this circumstance?

@jbesraa

jbesraa commented Feb 19, 2024

Copy link
Copy Markdown
Contributor Author

TBH maybe its best to close the PR as the scenario is not really feasible. what do you think?

@DanGould

Copy link
Copy Markdown
Member

This issue has been entrusted to you to resolve. However If it's going to be closed as wontfix please write up a thorough explanation as to why in a closing comment. It seems like there is still occasion where the downgrade happens on bip21 url parse vs on error which should still be handled by a v2 sender. So the PR was slightly misguided on this first attempt but the issue can still be resolved.

@jbesraa

jbesraa commented Feb 20, 2024

Copy link
Copy Markdown
Contributor Author

If a user is running v2, then their default is sending v2, we want to be able first to check if the receiver accepts v2, if not we should send v1.
So instead of trying twice, we can parse the uri and understand if the receiver accepts v2(by validating the ohttp parameter), if not, we can use the existing functionality to try and send v2.

Notice that there is a refactor(#198) in the cli, so pay attention to that before going on.

@jbesraa jbesraa closed this Feb 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Send v1 requests to v1 receivers even if v2 feature is enabled

2 participants