Skip to content

Commit

Permalink
errorCodes without serde
Browse files Browse the repository at this point in the history
  • Loading branch information
jbesraa committed Dec 12, 2023
1 parent 5dda85e commit 3618919
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 13 deletions.
7 changes: 6 additions & 1 deletion payjoin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ bhttp = { version = "0.4.0", optional = true }
rand = { version = "0.8.4", optional = true }
serde = { version = "1.0.186", default-features = false }
url = "2.2.2"
bitcoind = { version = "0.31.1", features = ["0_21_2"] }

[dev-dependencies]
bitcoind = { version = "0.31.1", features = ["0_21_2"] }
env_logger = "0.9.0"
rustls = "0.21.9"
testcontainers = "0.15.0"
Expand All @@ -42,3 +42,8 @@ ureq = "2.8.0"

[package.metadata.docs.rs]
features = ["send", "receive", "base64"]
env_logger = "0.9.0"


[lib]
doctest = false
2 changes: 1 addition & 1 deletion payjoin/src/receive/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::fmt::{self, Display};
pub enum Error {
/// To be returned as HTTP 400
BadRequest(RequestError),
// To be returned as HTTP 500
/// To be returned as HTTP 500
Server(Box<dyn error::Error>),
// V2 d/encapsulation failed
#[cfg(feature = "v2")]
Expand Down
73 changes: 64 additions & 9 deletions payjoin/src/send/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::str::FromStr;

use bitcoin::locktime::absolute::LockTime;
use bitcoin::Sequence;
use bitcoind::bitcoincore_rpc::jsonrpc::serde_json::Value;

use crate::input_type::{InputType, InputTypeError};

Expand Down Expand Up @@ -238,18 +239,19 @@ impl From<InternalCreateRequestError> for CreateRequestError {
}

pub enum ResponseError {
/// `WellKnown` errors following the BIP78 spec
/// https://github.com/bitcoin/bips/blob/master/bip-0078.mediawiki#user-content-Receivers_well_known_errors
/// `WellKnown` errors following the BIP78 spec.
///
/// These errors are displayed to end users.
///
/// The `WellKnownError` represents `errorCode` and `message`.
/// The `String` is a custom message that can be used for debug logs.
WellKnown(WellKnownError, String),
/// `Unrecognized` errors are errors that are not well known and are only displayed in debug logs.
/// `WellKnownError` represents `errorCode` and `message`.
/// `String` is a custom message that can be used for debug logs.
WellKnown(WellKnownError, Option<String>),
/// `Unrecognized` errors are errors that are not
/// mentioned in the spec and are only displayed in debug logs.
/// They are not displayed to end users.
///
/// The first `String` is `errorCode`
/// The second `String` is `message`.
/// First `String` is `errorCode`
/// Second `String` is `message`.
Unrecognized(String, String),
/// `Validation` errors are errors that are caused by malformed responses.
/// They are only displayed in debug logs.
Expand Down Expand Up @@ -281,7 +283,8 @@ impl Debug for ResponseError {
f,
r#"Well known error: {{ "errorCode": "{}",
"message": "{}" }}"#,
e, msg
e,
msg.clone().unwrap_or("".to_string())
),
Self::Unrecognized(code, msg) => write!(
f,
Expand All @@ -293,6 +296,8 @@ impl Debug for ResponseError {
}
}

impl std::error::Error for ResponseError {}

pub enum WellKnownError {
Unavailable,
NotEnoughMoney,
Expand Down Expand Up @@ -335,4 +340,54 @@ impl WellKnownError {
Self::OriginalPsbtRejected => "The receiver rejected the original PSBT.",
}
}

pub fn error_code_from_json(json: Value) -> Result<Self, ResponseError> {
let error_code = json
.as_object()
.and_then(|v| v.get("errorCode"))
.and_then(|v| v.as_str())
.ok_or(InternalValidationError::Parse)?;
WellKnownError::from_str(error_code).map_err(|_| {
let message = match json.get("message") {
Some(v) => v.to_string(),
None => {
log::debug!("Unrecognized Error detected, {}", json);
"Unrecognized Error detected".to_string()
}
};
ResponseError::Unrecognized(error_code.to_string(), message)
})
}

pub fn error_code_from_str(error_code: &str) -> Result<Self, ResponseError> {
Self::error_code_from_json(Value::from_str(&error_code).map_err(|e| {
log::debug!("Invalid json detected, {}", e);
InternalValidationError::Parse
})?)
}
}

#[cfg(test)]
mod tests {
use bitcoind::bitcoincore_rpc::jsonrpc::serde_json::json;

use super::*;

#[test]
fn test_parse_json() {
let json_error = json!({
"errorCode": "version-unsupported",
"message": "This version of payjoin is not supported."
});
assert_eq!(
WellKnownError::error_code_from_json(json_error).unwrap().to_string(),
WellKnownError::VersionUnsupported.to_string()
);
let str_error = "{\"errorCode\":\"version-unsupported\",
\"message\":\"This version of payjoin is not supported.\"}";
assert_eq!(
WellKnownError::error_code_from_str(str_error).unwrap().to_string(),
WellKnownError::VersionUnsupported.to_string()
);
}
}
10 changes: 8 additions & 2 deletions payjoin/src/send/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ use crate::PjUri;
#[cfg(not(any(target_pointer_width = "32", target_pointer_width = "64")))]
compile_error!("This crate currently only supports 32 bit and 64 bit architectures");

mod error;
pub mod error;

type InternalResult<T> = Result<T, InternalValidationError>;

Expand Down Expand Up @@ -536,7 +536,7 @@ impl ContextV1 {
let proposal = Psbt::from_str(&res_str).or_else(|_| {
// That wasn't a valid PSBT. Maybe it's a valid error response?
match WellKnownError::error_code_from_str(&res_str) {
Ok(well_known) => return Err(ResponseError::WellKnown(well_known, "".to_string())),
Ok(well_known) => return Err(ResponseError::WellKnown(well_known, None)),
Err(e) => Err(e),
}
})?;
Expand Down Expand Up @@ -907,6 +907,7 @@ fn serialize_v2_body(
disable_output_substitution,
fee_contribution,
min_feerate,
2,
)
.map_err(InternalCreateRequestError::Url)?;
let query_params = placeholder_url.query().unwrap_or_default();
Expand Down Expand Up @@ -938,6 +939,11 @@ fn serialize_url(
Ok(url)
}

pub fn serialize_psbt(psbt: &Psbt) -> Vec<u8> {
let bytes = psbt.serialize();
bitcoin::base64::encode(bytes).into_bytes()
}

#[cfg(test)]
mod tests {
#[test]
Expand Down

0 comments on commit 3618919

Please sign in to comment.