diff --git a/Cargo.lock b/Cargo.lock index 8ff4c431..172c0f44 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5708,6 +5708,8 @@ dependencies = [ "tokio", "tracing", "tracing-futures", + "zebra-network", + "zebra-test", ] [[package]] diff --git a/zebra-rpc/Cargo.toml b/zebra-rpc/Cargo.toml index 7caa0b95..0a97a19f 100644 --- a/zebra-rpc/Cargo.toml +++ b/zebra-rpc/Cargo.toml @@ -9,6 +9,8 @@ edition = "2021" [dependencies] +zebra-network = { path = "../zebra-network" } + futures = "0.3" # lightwalletd sends JSON-RPC requests over HTTP 1.1 @@ -24,3 +26,7 @@ tracing = "0.1" tracing-futures = "0.2" serde = { version = "1", features = ["serde_derive"] } + +[dev-dependencies] + +zebra-test = { path = "../zebra-test/" } diff --git a/zebra-rpc/src/methods.rs b/zebra-rpc/src/methods.rs index 7c07c041..c36cc57f 100644 --- a/zebra-rpc/src/methods.rs +++ b/zebra-rpc/src/methods.rs @@ -9,15 +9,34 @@ use jsonrpc_core::{self, Result}; use jsonrpc_derive::rpc; +use zebra_network::constants::USER_AGENT; + +#[cfg(test)] +mod tests; + #[rpc(server)] /// RPC method signatures. pub trait Rpc { /// getinfo /// - /// TODO: explain what the method does - /// link to the zcashd RPC reference - /// list the arguments and fields that lightwalletd uses - /// note any other lightwalletd changes + /// Returns software information from the RPC server running Zebra. + /// + /// zcashd reference: + /// + /// Result: + /// { + /// "build": String, // Full application version + /// "subversion", String, // Zebra user agent + /// } + /// + /// Note 1: We only expose 2 fields as they are the only ones needed for + /// lightwalletd: + /// + /// Note 2: is outdated so it does not + /// show the fields we are exposing. However, this fields are part of the output + /// as shown in the following zcashd code: + /// + /// Zcash open ticket to add this fields to the docs: #[rpc(name = "getinfo")] fn get_info(&self) -> Result; @@ -32,13 +51,16 @@ pub trait Rpc { } /// RPC method implementations. -pub struct RpcImpl; + +pub struct RpcImpl { + /// Zebra's application version. + pub app_version: String, +} impl Rpc for RpcImpl { fn get_info(&self) -> Result { - // TODO: dummy output data, fix in the context of #3142 let response = GetInfo { - build: "TODO: Zebra v1.0.0 ...".into(), - subversion: "TODO: /Zebra:1.0.0-beta.../".into(), + build: self.app_version.clone(), + subversion: USER_AGENT.into(), }; Ok(response) diff --git a/zebra-rpc/src/methods/tests.rs b/zebra-rpc/src/methods/tests.rs new file mode 100644 index 00000000..161e1472 --- /dev/null +++ b/zebra-rpc/src/methods/tests.rs @@ -0,0 +1,2 @@ +//! Test code for RPC methods +mod vectors; diff --git a/zebra-rpc/src/methods/tests/vectors.rs b/zebra-rpc/src/methods/tests/vectors.rs new file mode 100644 index 00000000..32b935e8 --- /dev/null +++ b/zebra-rpc/src/methods/tests/vectors.rs @@ -0,0 +1,23 @@ +//! Fixed test vectors for RPC methods. + +use super::super::*; +use zebra_network::constants::USER_AGENT; + +#[test] +fn rpc_getinfo() { + zebra_test::init(); + + let rpc = RpcImpl { + app_version: "Zebra version test".to_string(), + }; + + let get_info = rpc.get_info().expect("We should have a GetInfo struct"); + + // make sure there is a `build` field in the response, + // and that is equal to the provided string. + assert_eq!(get_info.build, "Zebra version test"); + + // make sure there is a `subversion` field, + // and that is equal to the Zebra user agent. + assert_eq!(get_info.subversion, USER_AGENT); +} diff --git a/zebra-rpc/src/server.rs b/zebra-rpc/src/server.rs index e180b327..9e6348ad 100644 --- a/zebra-rpc/src/server.rs +++ b/zebra-rpc/src/server.rs @@ -24,14 +24,17 @@ pub struct RpcServer; impl RpcServer { /// Start a new RPC server endpoint - pub fn spawn(config: Config) -> tokio::task::JoinHandle<()> { + pub fn spawn(config: Config, app_version: String) -> tokio::task::JoinHandle<()> { if let Some(listen_addr) = config.listen_addr { info!("Trying to open RPC endpoint at {}...", listen_addr,); + // Initialize the rpc methods with the zebra version + let rpc_impl = RpcImpl { app_version }; + // Create handler compatible with V1 and V2 RPC protocols let mut io = jsonrpc_core::IoHandler::with_compatibility(jsonrpc_core::Compatibility::Both); - io.extend_with(RpcImpl.to_delegate()); + io.extend_with(rpc_impl.to_delegate()); let server = ServerBuilder::new(io) // use the same tokio executor as the rest of Zebra @@ -54,6 +57,7 @@ impl RpcServer { info!("Stopping RPC endpoint"); }) }; + tokio::task::spawn_blocking(server) } else { // There is no RPC port, so the RPC task does nothing. diff --git a/zebrad/src/commands/start.rs b/zebrad/src/commands/start.rs index eed4f20f..62a06374 100644 --- a/zebrad/src/commands/start.rs +++ b/zebrad/src/commands/start.rs @@ -75,6 +75,7 @@ use zebra_consensus::CheckpointList; use zebra_rpc::server::RpcServer; use crate::{ + application::app_version, components::{ inbound::{self, InboundSetupData}, mempool::{self, Mempool}, @@ -196,7 +197,7 @@ impl StartCmd { .in_current_span(), ); - let rpc_task_handle = RpcServer::spawn(config.rpc); + let rpc_task_handle = RpcServer::spawn(config.rpc, app_version().to_string()); info!("spawned initial Zebra tasks");