Skip to content
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

feature(rpc): add an rpc server to Zebra #3589

Merged
merged 28 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
b51e994
feature(rpc): add an rpc component
oxarbitrage Feb 19, 2022
97c6a8e
Merge remote-tracking branch 'origin/main' into issue3140
oxarbitrage Feb 20, 2022
722e248
feat(rpc): add a stub for getblockchaininfo
teor2345 Feb 21, 2022
423f9b0
fix(rpc): remove non-standard "jsonrpc: 1.0" from lightwalletd
teor2345 Feb 21, 2022
b089d66
fix(rpc): re-enable default RPC security checks
teor2345 Feb 21, 2022
89ab602
Merge pull request #173 from ZcashFoundation/issue3140-compat
oxarbitrage Feb 21, 2022
5d744b5
deps(rpc): remove not needed dependency
oxarbitrage Feb 21, 2022
d170366
fix(rpc): check if RPC task has stopped
oxarbitrage Feb 21, 2022
27ca7ba
fix(rpc): reduce config by using Option
oxarbitrage Feb 21, 2022
e485523
fix(rpc): use tokio executor
oxarbitrage Feb 21, 2022
6ec258a
security(rpc): turn off rpc by default
oxarbitrage Feb 21, 2022
f7866f6
docs(rpc): update a TODO comment
oxarbitrage Feb 21, 2022
8ed2047
fix(rpc): blocking tasks
oxarbitrage Feb 21, 2022
c16b3be
rename(rpc): rpc.rs to methods.rs
oxarbitrage Feb 22, 2022
f8d8986
refactor(rpc): move the server to the zebra-rpc crate
oxarbitrage Feb 22, 2022
8f96bcb
Merge branch 'main' into issue3140
teor2345 Feb 22, 2022
70e31ff
fix(rpc): clippy derive Default for RPC Config
teor2345 Feb 22, 2022
d5738ac
fix(dependencies): remove unused dependency features in zebra-rpc
teor2345 Feb 22, 2022
8559d79
doc(rpc): fix testnet port, add security note
teor2345 Feb 22, 2022
d700133
fix(rpc): change Rust function names and update method doc TODOs
teor2345 Feb 22, 2022
c070f63
fix(rpc): add "TODO" to fake RPC responses
teor2345 Feb 22, 2022
b16bd55
doc(rpc): update module docs
teor2345 Feb 22, 2022
aa1dcc7
fix(rpc): simplify server struct derives
teor2345 Feb 22, 2022
1002ea6
fix(rpc): simplify server code
teor2345 Feb 22, 2022
84faa75
doc(rpc): explain how request fixes securely handle user-supplied data
teor2345 Feb 22, 2022
06057b2
refactor(rpc): move the compatibility fix to a separate module
teor2345 Feb 22, 2022
fb9cbb6
fix(rpc): move the open log inside the spawn, and instrument it
teor2345 Feb 22, 2022
49f563c
doc(rpc): fix toml format and provide a config example
teor2345 Feb 22, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions zebra-rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,19 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

futures = "0.3"

# lightwalletd sends JSON-RPC requests over HTTP 1.1
hyper = { version = "0.14.17", features = ["http1", "server"] }

jsonrpc-core = "18.0.0"
jsonrpc-derive = "18.0.0"
jsonrpc-http-server = "18.0.0"

tokio = { version = "1.16.1", features = ["time", "rt-multi-thread", "macros", "tracing"] }

tracing = "0.1"
tracing-futures = "0.2"

serde = { version = "1", features = ["serde_derive"] }
30 changes: 30 additions & 0 deletions zebra-rpc/src/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! User-configurable RPC settings.

use std::net::SocketAddr;

use serde::{Deserialize, Serialize};

/// RPC configuration section.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(deny_unknown_fields, default)]
pub struct Config {
/// IP address and port for the RPC server.
///
/// Note: The RPC server is disabled by default.
/// To enable the RPC server, set a listen address in the config:
/// ```toml
/// [rpc]
/// listen_addr = '127.0.0.1:8232'
/// ```
///
/// The recommended ports for the RPC server are:
/// - Mainnet: 127.0.0.1:8232
/// - Testnet: 127.0.0.1:18232
///
/// # Security
///
/// If you bind Zebra's RPC port to a public IP address,
/// anyone on the internet can send transactions via your node.
/// They can also query your node's state.
pub listen_addr: Option<SocketAddr>,
}
6 changes: 5 additions & 1 deletion zebra-rpc/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
//! A Zebra remote procedure call interface
//! A Zebra Remote Procedure Call (RPC) interface

#![doc(html_favicon_url = "https://www.zfnd.org/images/zebra-favicon-128.png")]
#![doc(html_logo_url = "https://www.zfnd.org/images/zebra-icon.png")]
#![doc(html_root_url = "https://doc.zebra.zfnd.org/zebra_rpc")]

pub mod config;
pub mod methods;
pub mod server;
69 changes: 69 additions & 0 deletions zebra-rpc/src/methods.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
//! Zebra supported RPC methods.
//!
//! Based on the [`zcashd` RPC methods](https://zcash.github.io/rpc/)
//! as used by `lightwalletd.`
//!
//! Some parts of the `zcashd` RPC documentation are outdated.
//! So this implementation follows the `lightwalletd` client implementation.

use jsonrpc_core::{self, Result};
use jsonrpc_derive::rpc;

#[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
#[rpc(name = "getinfo")]
fn get_info(&self) -> Result<GetInfo>;

/// getblockchaininfo
///
/// 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
#[rpc(name = "getblockchaininfo")]
fn get_blockchain_info(&self) -> Result<GetBlockChainInfo>;
}

/// RPC method implementations.
pub struct RpcImpl;
impl Rpc for RpcImpl {
fn get_info(&self) -> Result<GetInfo> {
// 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(),
};

Ok(response)
}

fn get_blockchain_info(&self) -> Result<GetBlockChainInfo> {
// TODO: dummy output data, fix in the context of #3143
let response = GetBlockChainInfo {
chain: "TODO: main".to_string(),
};

Ok(response)
}
}

#[derive(serde::Serialize, serde::Deserialize)]
/// Response to a `getinfo` RPC request.
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)
}
63 changes: 63 additions & 0 deletions zebra-rpc/src/server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
//! A JSON-RPC 1.0 & 2.0 endpoint for Zebra.
//!
//! This endpoint is compatible with clients that incorrectly send
//! `"jsonrpc" = 1.0` fields in JSON-RPC 1.0 requests,
//! such as `lightwalletd`.

use tracing::*;
use tracing_futures::Instrument;

use jsonrpc_core;
use jsonrpc_http_server::ServerBuilder;

use crate::{
config::Config,
methods::{Rpc, RpcImpl},
server::compatibility::FixHttpRequestMiddleware,
};

pub mod compatibility;

/// Zebra RPC Server
#[derive(Clone, Debug)]
pub struct RpcServer;

impl RpcServer {
/// Start a new RPC server endpoint
pub fn spawn(config: Config) -> tokio::task::JoinHandle<()> {
if let Some(listen_addr) = config.listen_addr {
info!("Trying to open RPC endpoint at {}...", listen_addr,);

// 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());

let server = ServerBuilder::new(io)
// use the same tokio executor as the rest of Zebra
.event_loop_executor(tokio::runtime::Handle::current())
.threads(1)
// TODO: disable this security check if we see errors from lightwalletd.
//.allowed_hosts(DomainsValidation::Disabled)
.request_middleware(FixHttpRequestMiddleware)
.start_http(&listen_addr)
.expect("Unable to start RPC server");

// The server is a blocking task, so we need to spawn it on a blocking thread.
let span = Span::current();
let server = move || {
span.in_scope(|| {
info!("Opened RPC endpoint at {}", server.address());

server.wait();

info!("Stopping RPC endpoint");
})
};
tokio::task::spawn_blocking(server)
} else {
// There is no RPC port, so the RPC task does nothing.
tokio::task::spawn(futures::future::pending().in_current_span())
}
}
}
Loading