-
Notifications
You must be signed in to change notification settings - Fork 39
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,7 +16,7 @@ pub struct ValidationError { | |
|
||
#[derive(Debug)] | ||
pub(crate) enum InternalValidationError { | ||
Psbt(bitcoin::psbt::PsbtParseError), | ||
Parse, | ||
Io(std::io::Error), | ||
InvalidInputType(InputTypeError), | ||
InvalidProposedInput(crate::psbt::PrevTxOutError), | ||
|
@@ -58,7 +58,6 @@ impl fmt::Display for ValidationError { | |
use InternalValidationError::*; | ||
|
||
match &self.internal { | ||
Psbt(e) => write!(f, "couldn't decode PSBT: {}", e), | ||
Io(e) => write!(f, "couldn't read PSBT: {}", e), | ||
InvalidInputType(e) => write!(f, "invalid transaction input type: {}", e), | ||
InvalidProposedInput(e) => write!(f, "invalid proposed transaction input: {}", e), | ||
|
@@ -86,6 +85,7 @@ impl fmt::Display for ValidationError { | |
PayeeTookContributedFee => write!(f, "payee tried to take fee contribution for himself"), | ||
FeeContributionPaysOutputSizeIncrease => write!(f, "fee contribution pays for additional outputs"), | ||
FeeRateBelowMinimum => write!(f, "the fee rate of proposed transaction is below minimum"), | ||
Parse => write!(f, "couldn't decode as PSBT or JSON",), | ||
} | ||
} | ||
} | ||
|
@@ -95,7 +95,6 @@ impl std::error::Error for ValidationError { | |
use InternalValidationError::*; | ||
|
||
match &self.internal { | ||
Psbt(error) => Some(error), | ||
Io(error) => Some(error), | ||
InvalidInputType(error) => Some(error), | ||
InvalidProposedInput(error) => Some(error), | ||
|
@@ -123,6 +122,7 @@ impl std::error::Error for ValidationError { | |
PayeeTookContributedFee => None, | ||
FeeContributionPaysOutputSizeIncrease => None, | ||
FeeRateBelowMinimum => None, | ||
Parse => None, | ||
} | ||
} | ||
} | ||
|
@@ -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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? |
||
WellKnown(WellKnownError, String), | ||
// Don't display unknowns to end users, only debug logs | ||
Unrecognized(String, String), | ||
|
||
Validation(ValidationError), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. missing some docs here, I feel that |
||
} | ||
|
||
impl ResponseError { | ||
pub fn from_json(json: &str) -> Self { | ||
use std::convert::TryInto; | ||
|
||
use tinyjson::{JsonParser, JsonValue}; | ||
|
||
let parsed: JsonValue = json.parse().unwrap(); | ||
//.unwrap_or_else( |_| ResponseError::Validation(InternalValidationError::Parse.into())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. commented code |
||
let maybe_code = parsed["errorCode"].get(); | ||
let maybe_message = parsed["message"].get(); | ||
if let (Some(error_code), Some(message)) = (maybe_code, maybe_message) { | ||
let well_known_error = WellKnownError::from_str(&error_code); | ||
|
||
if let Some(wk_error) = well_known_error { | ||
ResponseError::WellKnown(wk_error, message.to_string()) | ||
} else { | ||
ResponseError::Unrecognized(error_code.to_string(), message.to_string()) | ||
} | ||
} else { | ||
ResponseError::Validation(InternalValidationError::Parse.into()) | ||
} | ||
} | ||
} | ||
|
||
impl From<InternalValidationError> for ResponseError { | ||
fn from(value: InternalValidationError) -> Self { | ||
Self::Validation(ValidationError { internal: value }) | ||
} | ||
} | ||
|
||
// It is imperative to carefully display pre-defined messages to end users and the details in debug. | ||
impl fmt::Display for ResponseError { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
match self { | ||
Self::WellKnown(e, _) => e.fmt(f), | ||
// Don't display unknowns to end users, only debug logs | ||
Self::Unrecognized(_, _) => write!(f, "The receiver sent an unrecognized error."), | ||
Self::Validation(e) => write!(f, "The receiver sent an invalid response: {}", e), | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Debug for ResponseError { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
match self { | ||
Self::WellKnown(e, msg) => | ||
write!(f, r#"Well known error: {{ "errorCode": "{}", "message": "{}" }}"#, e, msg), | ||
Self::Unrecognized(code, msg) => write!( | ||
f, | ||
r#"Unrecognized error: {{ "errorCode": "{}", "message": "{}" }}"#, | ||
code, msg | ||
), | ||
Self::Validation(e) => write!(f, "Validation({:?})", e), | ||
} | ||
} | ||
} | ||
|
||
impl std::error::Error for ResponseError {} | ||
|
||
#[derive(Debug)] | ||
pub enum WellKnownError { | ||
Unavailable, | ||
NotEnoughMoney, | ||
VersionUnsupported, | ||
OriginalPsbtRejected, | ||
} | ||
|
||
impl WellKnownError { | ||
pub fn from_str(s: &str) -> Option<Self> { | ||
match s { | ||
"unavailable" => Some(WellKnownError::Unavailable), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
"not-enough-money" => Some(WellKnownError::NotEnoughMoney), | ||
"version-unsupported" => Some(WellKnownError::VersionUnsupported), | ||
"original-psbt-rejected" => Some(WellKnownError::OriginalPsbtRejected), | ||
_ => None, | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for WellKnownError { | ||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||
match self { | ||
Self::Unavailable => write!(f, "The payjoin endpoint is not available for now."), | ||
Self::NotEnoughMoney => write!(f, "The receiver added some inputs but could not bump the fee of the payjoin proposal."), | ||
Self::VersionUnsupported => write!(f, "This version of payjoin is not supported."), | ||
Self::OriginalPsbtRejected => write!(f, "The receiver rejected the original PSBT."), | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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 oferror
as well and then we wont have to add double log lines.for example, if we have these levels of logs: