-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(rpc): Make RPC "incorrect parameters" error code match
zcashd
(#…
…6066) * Move RPC method constants into their own module * Rename RPC compatibility modules to avoid confusion * Rename RPC middleware to include its new functionality * Use FutureExt::inspect() for logging, and only format on failure * Log all RPC errors at info level * Make "invalid parameters" RPC error code match `zcashd`
- Loading branch information
Showing
7 changed files
with
128 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
//! Constants for RPC methods and server responses. | ||
use jsonrpc_core::ErrorCode; | ||
|
||
/// The RPC error code used by `zcashd` for incorrect RPC parameters. | ||
/// | ||
/// [`jsonrpc_core`] uses these codes: | ||
/// <https://github.com/paritytech/jsonrpc/blob/609d7a6cc160742d035510fa89fb424ccf077660/core/src/types/error.rs#L25-L36> | ||
/// | ||
/// `node-stratum-pool` mining pool library expects error code `-1` to detect available RPC methods: | ||
/// <https://github.com/s-nomp/node-stratum-pool/blob/d86ae73f8ff968d9355bb61aac05e0ebef36ccb5/lib/pool.js#L459> | ||
pub const INVALID_PARAMETERS_ERROR_CODE: ErrorCode = ErrorCode::ServerError(-1); | ||
|
||
/// The RPC error code used by `zcashd` for missing blocks. | ||
/// | ||
/// `lightwalletd` expects error code `-8` when a block is not found: | ||
/// <https://github.com/adityapk00/lightwalletd/blob/c1bab818a683e4de69cd952317000f9bb2932274/common/common.go#L251-L254> | ||
pub const MISSING_BLOCK_ERROR_CODE: ErrorCode = ErrorCode::ServerError(-8); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
//! Compatibility fixes for JSON-RPC remote procedure calls. | ||
//! | ||
//! These fixes are applied at the JSON-RPC call level, | ||
//! after the RPC request is parsed and split into calls. | ||
use std::future::Future; | ||
|
||
use futures::future::{Either, FutureExt}; | ||
use jsonrpc_core::{ | ||
middleware::Middleware, | ||
types::{Call, Failure, Output, Response}, | ||
BoxFuture, ErrorCode, Metadata, MethodCall, Notification, | ||
}; | ||
|
||
use crate::constants::INVALID_PARAMETERS_ERROR_CODE; | ||
|
||
/// JSON-RPC [`Middleware`] with compatibility workarounds. | ||
/// | ||
/// This middleware makes the following changes to JSON-RPC calls: | ||
/// | ||
/// ## Make RPC framework response codes match `zcashd` | ||
/// | ||
/// [`jsonrpc_core`] returns specific error codes while parsing requests: | ||
/// <https://docs.rs/jsonrpc-core/18.0.0/jsonrpc_core/types/error/enum.ErrorCode.html#variants> | ||
/// | ||
/// But these codes are different from `zcashd`, and some RPC clients rely on the exact code. | ||
/// | ||
/// ## Read-Only Functionality | ||
/// | ||
/// This middleware also logs unrecognized RPC requests. | ||
pub struct FixRpcResponseMiddleware; | ||
|
||
impl<M: Metadata> Middleware<M> for FixRpcResponseMiddleware { | ||
type Future = BoxFuture<Option<Response>>; | ||
type CallFuture = BoxFuture<Option<Output>>; | ||
|
||
fn on_call<Next, NextFuture>( | ||
&self, | ||
call: Call, | ||
meta: M, | ||
next: Next, | ||
) -> Either<Self::CallFuture, NextFuture> | ||
where | ||
Next: Fn(Call, M) -> NextFuture + Send + Sync, | ||
NextFuture: Future<Output = Option<Output>> + Send + 'static, | ||
{ | ||
Either::Left( | ||
next(call.clone(), meta) | ||
.map(|mut output| { | ||
Self::fix_error_codes(&mut output); | ||
output | ||
}) | ||
.inspect(|output| Self::log_if_error(output, call)) | ||
.boxed(), | ||
) | ||
} | ||
} | ||
|
||
impl FixRpcResponseMiddleware { | ||
/// Replace [`jsonrpc_core`] server error codes in `output` with the `zcashd` equivalents. | ||
fn fix_error_codes(output: &mut Option<Output>) { | ||
if let Some(Output::Failure(Failure { ref mut error, .. })) = output { | ||
if matches!(error.code, ErrorCode::InvalidParams) { | ||
let original_code = error.code.clone(); | ||
|
||
error.code = INVALID_PARAMETERS_ERROR_CODE; | ||
tracing::debug!("Replacing RPC error: {original_code:?} with {error}"); | ||
} | ||
} | ||
} | ||
|
||
/// Obtain a description string for a received request. | ||
/// | ||
/// Prints out only the method name and the received parameters. | ||
fn call_description(call: &Call) -> String { | ||
match call { | ||
Call::MethodCall(MethodCall { method, params, .. }) => { | ||
format!(r#"method = {method:?}, params = {params:?}"#) | ||
} | ||
Call::Notification(Notification { method, params, .. }) => { | ||
format!(r#"notification = {method:?}, params = {params:?}"#) | ||
} | ||
Call::Invalid { .. } => "invalid request".to_owned(), | ||
} | ||
} | ||
|
||
/// Check RPC output and log any errors. | ||
// | ||
// TODO: do we want to ignore ErrorCode::ServerError(_), or log it at debug? | ||
fn log_if_error(output: &Option<Output>, call: Call) { | ||
if let Some(Output::Failure(Failure { error, .. })) = output { | ||
let call_description = Self::call_description(&call); | ||
tracing::info!("RPC error: {error} in call: {call_description}"); | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.