Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Supply & parse receiver response errors #110

Closed
wants to merge 3 commits into from

Conversation

DanGould
Copy link
Contributor

@DanGould DanGould commented Oct 17, 2023

@@ -22,6 +22,8 @@ bitcoin = { version = "0.30.0", features = ["base64"] }
bip21 = "0.3.1"
log = { version = "0.4.14"}
rand = { version = "0.8.4", optional = true }
serde = { version = "1.0.107", features = ["derive"] }
Copy link
Contributor

@jbesraa jbesraa Oct 19, 2023

Choose a reason for hiding this comment

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

do we have better options than serde?
I have some reservations in adding it to such a project for the following reasons:

  1. a couple of months ago this thing happened https://www.bleepingcomputer.com/news/security/rust-devs-push-back-as-serde-project-ships-precompiled-binaries/
    it was eventually reverted but it is a red flag
  2. serde does a great job but it also makes compilation/building the project much slower

Copy link
Contributor Author

@DanGould DanGould Oct 19, 2023

Choose a reason for hiding this comment

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

We only need to parse and produce 2 lines of valid json without anything fancy like nesting. What do you suggest as an alternative, and why?

The json crate would require us to lift more for deserialization but is lightweight in comparison to serde https://github.com/maciejhirsz/json-rust

We should pay attention to how rust-bitcoin desides to handle this even though their use case is optional vs. protocol-critical here rust-bitcoin/rust-bitcoin#2009

Copy link
Contributor

Choose a reason for hiding this comment

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

we could consider https://github.com/rhysd/tinyjson

  1. its well documented, tested and the code is quite readable
  2. its really tiny(we can also clone it into payjoin repo and strip out any code we dont need, we just parse string really)

disadvantages: last update was a few months ago, but that's not necessarily a red flag

I think optimal scenario is to clone tinyjson, clean it up so it has only functionality we need and maintain it ourselves.

maybe we can also leave serde and put it behind a flag for now.

it really depends on how fast we want this pr to be merged

*I am intentionally not mentioning performance here because we are not dealing with large/nested json structures so that should be a problem

Copy link
Contributor

Choose a reason for hiding this comment

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

regarding json-rust, its not maintained and the last update was 3 years ago, an alternative crates were developed(like rust-json)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think optimal scenario is to clone tinyjson, clean it up so it has only functionality we need and maintain it ourselves.

This is definitely overkill to parse 4 lines of JSON that should only ever encode / decode strings with errorCode and message fields. We could honestly write an even simpler parser and forego a JSON dependency without a problem.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We might miss some edge cases, but we could simplify ser/de do the following

fn serialize_error(error_code: &str, message: &str) -> String {
    format!(r#"{{"errorCode": "{}", "message": "{}"}}"#, error_code, message)
}

above is how we've already been serializing errors

fn deserialize_error(json: &str) -> Option<(&str, &str)> {
    let error_code_start = json.find("\"errorCode\": \"")? + "\"errorCode\": \"".len();
    let error_code_end = json.find("\", \"message\": \"")?;
    let message_start = error_code_end + "\", \"message\": \"".len();
    let message_end = json.rfind("\"}")?;

    let error_code = &json[error_code_start..error_code_end];
    let message = &json[message_start..message_end];

    Some((error_code, message))
}

@DanGould
Copy link
Contributor Author

I started to add tinyjson but got stuck deserializing. The value["index"] syntax will panic and we need it too return errors. The latest source provides .query() but it's not released yet

Comment on lines +112 to +113
log::error!("Response contains error: {}", e);
log::debug!("Response contains error: {:?}", e);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe we could have the definition of debug execute all the logs of error as well and then we wont have to add double log lines.
for example, if we have these levels of logs:

  • Debug <- show everything Trace is showing + some more logs
  • Trace <- show everything Error is showing + some more logs
  • Error <- show only errors

impl WellKnownError {
pub fn from_str(s: &str) -> Option<Self> {
match s {
"unavailable" => Some(WellKnownError::Unavailable),
Copy link
Contributor

Choose a reason for hiding this comment

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

validated the errors and their description against https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#receivers-well-known-errors
looks good!

use tinyjson::{JsonParser, JsonValue};

let parsed: JsonValue = json.parse().unwrap();
//.unwrap_or_else( |_| ResponseError::Validation(InternalValidationError::Parse.into()));
Copy link
Contributor

Choose a reason for hiding this comment

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

commented code

@@ -210,3 +210,102 @@ impl std::error::Error for CreateRequestError {
impl From<InternalCreateRequestError> for CreateRequestError {
fn from(value: InternalCreateRequestError) -> Self { CreateRequestError(value) }
}

pub enum ResponseError {
// Well known errors with internal message for logs as String
Copy link
Contributor

Choose a reason for hiding this comment

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

should we add a bit more info here and maybe link to bip78 for reference?
also, should we use /// in the comments so it shows also on docs.rs ?

// Don't display unknowns to end users, only debug logs
Unrecognized(String, String),

Validation(ValidationError),
Copy link
Contributor

Choose a reason for hiding this comment

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

missing some docs here, I feel that Validation is quite broad

@jbesraa
Copy link
Contributor

jbesraa commented Nov 10, 2023

ah just saw your comment regarding tinyjson, how do you want to proceed with that?

@DanGould
Copy link
Contributor Author

closed for #120

DanGould added a commit that referenced this pull request Dec 27, 2023
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.

Supply receiver Well Known Errrors
2 participants