-
Notifications
You must be signed in to change notification settings - Fork 405
Surface bitcoind rpc error information #2057
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,8 +13,27 @@ use serde_json; | |
|
||
use std::convert::TryFrom; | ||
use std::convert::TryInto; | ||
use std::error::Error; | ||
use std::fmt; | ||
use std::sync::atomic::{AtomicUsize, Ordering}; | ||
|
||
/// An error returned by the RPC server. | ||
#[derive(Debug)] | ||
pub struct RpcError { | ||
/// The error code. | ||
pub code: i64, | ||
/// The error message. | ||
pub message: String, | ||
} | ||
|
||
impl fmt::Display for RpcError { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
write!(f, "RPC error {}: {}", self.code, self.message) | ||
} | ||
} | ||
|
||
impl Error for RpcError {} | ||
|
||
/// A simple RPC client for calling methods using HTTP `POST`. | ||
pub struct RpcClient { | ||
basic_auth: String, | ||
|
@@ -69,8 +88,11 @@ impl RpcClient { | |
let error = &response["error"]; | ||
if !error.is_null() { | ||
// TODO: Examine error code for a more precise std::io::ErrorKind. | ||
let message = error["message"].as_str().unwrap_or("unknown error"); | ||
return Err(std::io::Error::new(std::io::ErrorKind::Other, message)); | ||
let rpc_error = RpcError { | ||
code: error["code"].as_i64().unwrap_or(-1), | ||
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 make 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. yeah I guess we could do that. I'm not sure what case (if ever) it won't have a code and message. I purposefully mapped it to RPC_MISC_ERROR since that's sort of what it is but happy to do it as Option's too if you think that makes more sense. 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. maybe it'd be a little cleaner to make RpcError an enum with a struct variant and an "Unknown" variant when there's no code/message 🤷♂️ I guess you don't gain much, still need to do a match to find out error code 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. Let's keep it as is. Like you said, it probably shouldn't ever happen and the code is used as a catch-all for when the RPC handling code throws an exception in Core. 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. Ok, Matt had made a comment that we should probably just use a new RpcError enum that wraps the io Error. think it’s worth just doing that? 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. That's a much larger change and is orthogonal, IMO, if you're just trying to include an extra piece of data. Changing the error type here means would make it inconsistent with 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. Yeah, that makes sense. I was trying to avoid having to deal with a cascade of changes like that. If we're okay with this solution then it would work for what we needed (access to bitcoind error code) |
||
message: error["message"].as_str().unwrap_or("unknown error").to_string() | ||
}; | ||
return Err(std::io::Error::new(std::io::ErrorKind::Other, rpc_error)); | ||
} | ||
|
||
let result = &mut response["result"]; | ||
|
@@ -163,7 +185,9 @@ mod tests { | |
match client.call_method::<u64>("getblock", &[invalid_block_hash]).await { | ||
Err(e) => { | ||
assert_eq!(e.kind(), std::io::ErrorKind::Other); | ||
assert_eq!(e.get_ref().unwrap().to_string(), "invalid parameter"); | ||
let rpc_error: Box<RpcError> = e.into_inner().unwrap().downcast().unwrap(); | ||
assert_eq!(rpc_error.code, -8); | ||
assert_eq!(rpc_error.message, "invalid parameter"); | ||
}, | ||
Ok(_) => panic!("Expected error"), | ||
} | ||
|
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.
Looking into this a little more, I think the idea was that some errors may be considered transient while others are persistent. And this would affect the conversion done here:
rust-lightning/lightning-block-sync/src/convert.rs
Lines 21 to 29 in 8311581
This let's the user know if they should give-up or retry.
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.
Ah ok, that makes sense. Well I can put the TODO back. Seems like to actually impl the TODO we would need to map the error rpc error codes into buckets for persistent vs transient.