From 722e24831a21c5870d2c22d13c5a51363b451a9e Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 21 Feb 2022 12:40:38 +1000 Subject: [PATCH 1/3] feat(rpc): add a stub for getblockchaininfo This is the first RPC used by lightwalletd, so we need it for testing. --- zebra-rpc/src/rpc.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/zebra-rpc/src/rpc.rs b/zebra-rpc/src/rpc.rs index ef107f869d9..57ba4ee8889 100644 --- a/zebra-rpc/src/rpc.rs +++ b/zebra-rpc/src/rpc.rs @@ -11,6 +11,10 @@ pub trait Rpc { /// getinfo #[rpc(name = "getinfo")] fn getinfo(&self) -> Result; + + /// getblockchaininfo + #[rpc(name = "getblockchaininfo")] + fn getblockchaininfo(&self) -> Result; } /// RPC method implementations. @@ -25,6 +29,15 @@ impl Rpc for RpcImpl { Ok(info) } + + fn getblockchaininfo(&self) -> Result { + // TODO: dummy output data, fix in the context of #3143 + let response = GetBlockChainInfo { + chain: "main".to_string(), + }; + + Ok(response) + } } #[derive(serde::Serialize, serde::Deserialize)] @@ -33,3 +46,10 @@ pub struct GetInfo { build: String, subversion: String, } + +#[derive(serde::Serialize, serde::Deserialize)] +/// Response to a `getblockchaininfo` RPC request. +pub struct GetBlockChainInfo { + chain: String, + // TODO: add other fields used by lightwalletd (#3143) +} From 423f9b098555123cc7add0a80148b9f3b3f997d4 Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 21 Feb 2022 12:43:41 +1000 Subject: [PATCH 2/3] fix(rpc): remove non-standard "jsonrpc: 1.0" from lightwalletd --- zebrad/src/components/rpc.rs | 79 +++++++++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/zebrad/src/components/rpc.rs b/zebrad/src/components/rpc.rs index c9760a1e544..945624640cb 100644 --- a/zebrad/src/components/rpc.rs +++ b/zebrad/src/components/rpc.rs @@ -1,8 +1,9 @@ //! An RPC endpoint. +use futures::TryStreamExt; +use hyper::{body::Bytes, Body}; use jsonrpc_core; - -use jsonrpc_http_server::ServerBuilder; +use jsonrpc_http_server::{DomainsValidation, RequestMiddleware, ServerBuilder}; use zebra_rpc::rpc::{Rpc, RpcImpl}; @@ -24,7 +25,12 @@ impl RpcServer { io.extend_with(RpcImpl.to_delegate()); let server = ServerBuilder::new(io) + // TODO: use the same tokio executor as the rest of Zebra + //.event_loop_executor(tokio::runtime::Handle::current()) .threads(1) + // TODO: if we enable this security check, does lightwalletd still work? + .allowed_hosts(DomainsValidation::Disabled) + .request_middleware(FixHttpRequestMiddleware) .start_http(&config.listen_addr) .expect("Unable to start RPC server"); @@ -35,3 +41,72 @@ impl RpcServer { RpcServer {} } } + +/// HTTP [`RequestMiddleware`] with compatibility wrokarounds. +/// +/// This middleware makes the following changes to requests: +/// +/// ## JSON RPC 1.0 `jsonrpc` field +/// +/// Removes "jsonrpc: 1.0" fields from requests, +/// because the "jsonrpc" field was only added in JSON-RPC 2.0. +/// +/// +// +// TODO: put this HTTP middleware in a separate module +#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)] +pub struct FixHttpRequestMiddleware; + +impl RequestMiddleware for FixHttpRequestMiddleware { + fn on_request( + &self, + request: hyper::Request, + ) -> jsonrpc_http_server::RequestMiddlewareAction { + let request = request.map(|body| { + let body = body.map_ok(|data| { + // To simplify data handling, we assume that any search strings won't be split + // across multiple `Bytes` data buffers. + // + // To simplify error handling, Zebra only supports valid UTF-8 requests, + // and uses lossy UTF-8 conversion. + // + // JSON-RPC requires all requests to be valid UTF-8. + // The lower layers should reject invalid requests with lossy changes. + // But if they accept some lossy changes, that's ok, + // because the request was non-standard anyway. + // + // We're not concerned about performance here, so we just clone the Cow + let data = String::from_utf8_lossy(data.as_ref()).to_string(); + + // Fix up the request. + let data = Self::remove_json_1_fields(data); + + Bytes::from(data) + }); + + Body::wrap_stream(body) + }); + + jsonrpc_http_server::RequestMiddlewareAction::Proceed { + // TODO: if we enable this security check, does lightwalletd still work? + should_continue_on_invalid_cors: true, + request, + } + } +} + +impl FixHttpRequestMiddleware { + /// Remove any "jsonrpc: 1.0" fields in `data`, and return the resulting string. + pub fn remove_json_1_fields(data: String) -> String { + // Replace "jsonrpc = 1.0": + // - at the start or middle of a list, and + // - at the end of a list; + // with no spaces (lightwalletd format), and spaces after separators (example format). + // + // TODO: replace this with a regular expression if we see errors from lightwalletd. + data.replace("\"jsonrpc\":\"1.0\",", "") + .replace("\"jsonrpc\": \"1.0\",", "") + .replace(",\"jsonrpc\":\"1.0\"", "") + .replace(", \"jsonrpc\": \"1.0\"", "") + } +} From b089d665ccd4bce00dbda30243626b87aea2ccb1 Mon Sep 17 00:00:00 2001 From: teor Date: Mon, 21 Feb 2022 12:47:16 +1000 Subject: [PATCH 3/3] fix(rpc): re-enable default RPC security checks --- zebrad/src/components/rpc.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zebrad/src/components/rpc.rs b/zebrad/src/components/rpc.rs index 945624640cb..cd212419dd8 100644 --- a/zebrad/src/components/rpc.rs +++ b/zebrad/src/components/rpc.rs @@ -3,7 +3,7 @@ use futures::TryStreamExt; use hyper::{body::Bytes, Body}; use jsonrpc_core; -use jsonrpc_http_server::{DomainsValidation, RequestMiddleware, ServerBuilder}; +use jsonrpc_http_server::{RequestMiddleware, ServerBuilder}; use zebra_rpc::rpc::{Rpc, RpcImpl}; @@ -28,8 +28,8 @@ impl RpcServer { // TODO: use the same tokio executor as the rest of Zebra //.event_loop_executor(tokio::runtime::Handle::current()) .threads(1) - // TODO: if we enable this security check, does lightwalletd still work? - .allowed_hosts(DomainsValidation::Disabled) + // TODO: disable this security check if we see errors from lightwalletd. + //.allowed_hosts(DomainsValidation::Disabled) .request_middleware(FixHttpRequestMiddleware) .start_http(&config.listen_addr) .expect("Unable to start RPC server"); @@ -88,8 +88,8 @@ impl RequestMiddleware for FixHttpRequestMiddleware { }); jsonrpc_http_server::RequestMiddlewareAction::Proceed { - // TODO: if we enable this security check, does lightwalletd still work? - should_continue_on_invalid_cors: true, + // TODO: disable this security check if we see errors from lightwalletd. + should_continue_on_invalid_cors: false, request, } }