diff --git a/parity-clib-example/main.cpp b/parity-clib-example/main.cpp index 1fadf1b5b..becce8598 100644 --- a/parity-clib-example/main.cpp +++ b/parity-clib-example/main.cpp @@ -1,4 +1,7 @@ #include +#include +#include +#include #include #include @@ -8,8 +11,8 @@ int main() { ParityParams cfg = { 0 }; cfg.on_client_restart_cb = on_restart; - const char* args[] = {"--light"}; - size_t str_lens[] = {7}; + const char* args[] = {"--no-ipc"}; + size_t str_lens[] = {8}; if (parity_config_from_cli(args, str_lens, 1, &cfg.configuration) != 0) { return 1; } @@ -19,6 +22,16 @@ int main() { return 1; } + const char* rpc = "{\"method\":\"parity_versionInfo\",\"params\":[],\"id\":1,\"jsonrpc\":\"2.0\"}"; + size_t out_len = 256; + char* out = (char*)malloc(out_len + 1); + if (parity_rpc(parity, rpc, strlen(rpc), out, &out_len)) { + return 1; + } + out[out_len] = '\0'; + printf("RPC output: %s", out); + free(out); + sleep(5); if (parity != NULL) { parity_destroy(parity); diff --git a/parity-clib/parity.h b/parity-clib/parity.h index b61da8e45..f647395ce 100644 --- a/parity-clib/parity.h +++ b/parity-clib/parity.h @@ -86,6 +86,22 @@ int parity_start(const ParityParams* params, void** out); /// must not call this function. void parity_destroy(void* parity); +/// Performs an RPC request. +/// +/// Blocks the current thread until the request is finished. You are therefore encouraged to spawn +/// a new thread for each RPC request that requires accessing the blockchain. +/// +/// - `rpc` and `len` must contain the JSON string representing the RPC request. +/// - `out_str` and `out_len` point to a buffer where the output JSON result will be stored. If the +/// buffer is not large enough, the function fails. +/// - `out_len` will receive the final length of the string. +/// - On success, the function returns 0. On failure, it returns 1. +/// +/// **Important**: Keep in mind that this function doesn't write any null terminator on the output +/// string. +/// +int parity_rpc(void* parity, const char* rpc, size_t len, char* out_str, size_t* out_len); + #ifdef __cplusplus } #endif diff --git a/parity-clib/src/lib.rs b/parity-clib/src/lib.rs index b08d6487d..fe631ce8a 100644 --- a/parity-clib/src/lib.rs +++ b/parity-clib/src/lib.rs @@ -23,6 +23,7 @@ use std::os::raw::{c_char, c_void, c_int}; use std::panic; use std::ptr; use std::slice; +use std::str; #[repr(C)] pub struct ParityParams { @@ -131,3 +132,33 @@ pub extern fn parity_destroy(client: *mut c_void) { }); } } + +#[no_mangle] +pub extern fn parity_rpc(client: *mut c_void, query: *const char, len: usize, out_str: *mut c_char, out_len: *mut usize) -> c_int { + unsafe { + panic::catch_unwind(|| { + let client: &mut parity::RunningClient = &mut *(client as *mut parity::RunningClient); + + let query_str = { + let string = slice::from_raw_parts(query as *const u8, len); + match str::from_utf8(string) { + Ok(a) => a, + Err(_) => return 1, + } + }; + + if let Some(output) = client.rpc_query_sync(query_str) { + let q_out_len = output.as_bytes().len(); + if *out_len < q_out_len { + return 1; + } + + ptr::copy_nonoverlapping(output.as_bytes().as_ptr(), out_str as *mut u8, q_out_len); + *out_len = q_out_len; + 0 + } else { + 1 + } + }).unwrap_or(1) + } +} diff --git a/parity/rpc.rs b/parity/rpc.rs index 66a2715d4..21bc9a409 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -355,7 +355,7 @@ fn with_domain(items: Option>, domain: &str, ui_address: &Option(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler> +pub fn setup_apis(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler> where D: rpc_apis::Dependencies { let mut handler = MetaIoHandler::with_middleware( diff --git a/parity/run.rs b/parity/run.rs index 73113055b..fd16085c4 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -41,7 +41,7 @@ use miner::external::ExternalMiner; use node_filter::NodeFilter; use node_health; use parity_reactor::EventLoop; -use parity_rpc::{NetworkSettings, informant, is_major_importing}; +use parity_rpc::{Origin, Metadata, NetworkSettings, informant, is_major_importing}; use updater::{UpdatePolicy, Updater}; use parity_version::version; use ethcore_private_tx::{ProviderConfig, EncryptorConfig, SecretStoreEncryptor}; @@ -56,6 +56,7 @@ use cache::CacheConfig; use user_defaults::UserDefaults; use dapps; use ipfs; +use jsonrpc_core; use modules; use rpc; use rpc_apis; @@ -369,6 +370,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc) -> Result) -> Result(cmd: RunCmd, logger: Arc, on_client_rq: }; // start rpc servers + let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies); let ws_server = rpc::new_ws(cmd.ws_conf.clone(), &dependencies)?; let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?; let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies, dapps_middleware)?; @@ -878,6 +882,7 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: Ok(RunningClient { inner: RunningClientInner::Full { + rpc: rpc_direct, informant, client, keep_alive: Box::new((watcher, service, updater, ws_server, http_server, ipc_server, ui_server, secretstore_key_server, ipfs_server, event_loop)), @@ -890,16 +895,18 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: /// Should be destroyed by calling `shutdown()`, otherwise execution will continue in the /// background. pub struct RunningClient { - inner: RunningClientInner + inner: RunningClientInner, } enum RunningClientInner { Light { + rpc: jsonrpc_core::MetaIoHandler>, informant: Arc>, client: Arc, keep_alive: Box, }, Full { + rpc: jsonrpc_core::MetaIoHandler>, informant: Arc>, client: Arc, keep_alive: Box, @@ -907,25 +914,45 @@ enum RunningClientInner { } impl RunningClient { + /// Performs a synchronous RPC query. + /// Blocks execution until the result is ready. + pub fn rpc_query_sync(&self, request: &str) -> Option { + let metadata = Metadata { + origin: Origin::CApi, + session: None, + }; + + match self.inner { + RunningClientInner::Light { ref rpc, .. } => { + rpc.handle_request_sync(request, metadata) + }, + RunningClientInner::Full { ref rpc, .. } => { + rpc.handle_request_sync(request, metadata) + }, + } + } + /// Shuts down the client. pub fn shutdown(self) { match self.inner { - RunningClientInner::Light { informant, client, keep_alive } => { + RunningClientInner::Light { rpc, informant, client, keep_alive } => { // Create a weak reference to the client so that we can wait on shutdown // until it is dropped let weak_client = Arc::downgrade(&client); + drop(rpc); drop(keep_alive); informant.shutdown(); drop(informant); drop(client); wait_for_drop(weak_client); }, - RunningClientInner::Full { informant, client, keep_alive } => { + RunningClientInner::Full { rpc, informant, client, keep_alive } => { info!("Finishing work, please wait..."); // Create a weak reference to the client so that we can wait on shutdown // until it is dropped let weak_client = Arc::downgrade(&client); // drop this stuff as soon as exit detected. + drop(rpc); drop(keep_alive); // to make sure timer does not spawn requests while shutdown is in progress informant.shutdown(); diff --git a/rpc/src/v1/types/provenance.rs b/rpc/src/v1/types/provenance.rs index b52f0cb77..6bcd43a21 100644 --- a/rpc/src/v1/types/provenance.rs +++ b/rpc/src/v1/types/provenance.rs @@ -49,6 +49,9 @@ pub enum Origin { /// Session id session: H256 }, + /// From the C API + #[serde(rename="c-api")] + CApi, /// Unknown #[serde(rename="unknown")] Unknown, @@ -68,6 +71,7 @@ impl fmt::Display for Origin { Origin::Ipc(ref session) => write!(f, "IPC (session: {})", session), Origin::Ws { ref session, ref dapp } => write!(f, "{} via WebSocket (session: {})", dapp, session), Origin::Signer { ref session, ref dapp } => write!(f, "{} via UI (session: {})", dapp, session), + Origin::CApi => write!(f, "C API"), Origin::Unknown => write!(f, "unknown origin"), } }