Skip to content
This repository was archived by the owner on Nov 6, 2020. It is now read-only.

Allow making direct RPC queries from the C API #8588

Merged
merged 1 commit into from
May 22, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
17 changes: 15 additions & 2 deletions parity-clib-example/main.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <parity.h>

Expand All @@ -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"};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed --light because parity_versionInfo isn't available for the light client (don't really know why).
The --no-ipc is here just to give an example usage for the arguments.

size_t str_lens[] = {8};
if (parity_config_from_cli(args, str_lens, 1, &cfg.configuration) != 0) {
return 1;
}
Expand All @@ -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);
Expand Down
16 changes: 16 additions & 0 deletions parity-clib/parity.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 31 additions & 0 deletions parity-clib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
}
2 changes: 1 addition & 1 deletion parity/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,7 @@ fn with_domain(items: Option<Vec<String>>, domain: &str, ui_address: &Option<rpc
})
}

fn setup_apis<D>(apis: ApiSet, deps: &Dependencies<D>) -> MetaIoHandler<Metadata, Middleware<D::Notifier>>
pub fn setup_apis<D>(apis: ApiSet, deps: &Dependencies<D>) -> MetaIoHandler<Metadata, Middleware<D::Notifier>>
where D: rpc_apis::Dependencies
{
let mut handler = MetaIoHandler::with_middleware(
Expand Down
35 changes: 31 additions & 4 deletions parity/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
Expand Down Expand Up @@ -369,6 +370,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
};

// start rpc servers
let rpc_direct = rpc::setup_apis(rpc_apis::ApiSet::All, &dependencies);
let ws_server = rpc::new_ws(cmd.ws_conf, &dependencies)?;
let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies, dapps_middleware)?;
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
Expand All @@ -390,6 +392,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin

Ok(RunningClient {
inner: RunningClientInner::Light {
rpc: rpc_direct,
informant,
client,
keep_alive: Box::new((event_loop, service, ws_server, http_server, ipc_server, ui_server)),
Expand Down Expand Up @@ -805,6 +808,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, 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)?;
Expand Down Expand Up @@ -878,6 +882,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, 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)),
Expand All @@ -890,42 +895,64 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, 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<Metadata, informant::Middleware<rpc_apis::LightClientNotifier>>,
informant: Arc<Informant<LightNodeInformantData>>,
client: Arc<LightClient>,
keep_alive: Box<Any>,
},
Full {
rpc: jsonrpc_core::MetaIoHandler<Metadata, informant::Middleware<informant::ClientNotifier>>,
informant: Arc<Informant<FullNodeInformantData>>,
client: Arc<Client>,
keep_alive: Box<Any>,
},
}

impl RunningClient {
/// Performs a synchronous RPC query.
/// Blocks execution until the result is ready.
pub fn rpc_query_sync(&self, request: &str) -> Option<String> {
let metadata = Metadata {
origin: Origin::CApi,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a question

are these origins being used somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used in the transaction confirmations, and also I think for logging.

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();
Expand Down
4 changes: 4 additions & 0 deletions rpc/src/v1/types/provenance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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"),
}
}
Expand Down