Allow making direct RPC queries from the C API (#8588)
This commit is contained in:
parent
fe5f5b28d9
commit
bd1e3fc606
@ -1,4 +1,7 @@
|
|||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <parity.h>
|
#include <parity.h>
|
||||||
|
|
||||||
@ -8,8 +11,8 @@ int main() {
|
|||||||
ParityParams cfg = { 0 };
|
ParityParams cfg = { 0 };
|
||||||
cfg.on_client_restart_cb = on_restart;
|
cfg.on_client_restart_cb = on_restart;
|
||||||
|
|
||||||
const char* args[] = {"--light"};
|
const char* args[] = {"--no-ipc"};
|
||||||
size_t str_lens[] = {7};
|
size_t str_lens[] = {8};
|
||||||
if (parity_config_from_cli(args, str_lens, 1, &cfg.configuration) != 0) {
|
if (parity_config_from_cli(args, str_lens, 1, &cfg.configuration) != 0) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -19,6 +22,16 @@ int main() {
|
|||||||
return 1;
|
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);
|
sleep(5);
|
||||||
if (parity != NULL) {
|
if (parity != NULL) {
|
||||||
parity_destroy(parity);
|
parity_destroy(parity);
|
||||||
|
@ -86,6 +86,22 @@ int parity_start(const ParityParams* params, void** out);
|
|||||||
/// must not call this function.
|
/// must not call this function.
|
||||||
void parity_destroy(void* parity);
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -23,6 +23,7 @@ use std::os::raw::{c_char, c_void, c_int};
|
|||||||
use std::panic;
|
use std::panic;
|
||||||
use std::ptr;
|
use std::ptr;
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
use std::str;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct ParityParams {
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -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
|
where D: rpc_apis::Dependencies
|
||||||
{
|
{
|
||||||
let mut handler = MetaIoHandler::with_middleware(
|
let mut handler = MetaIoHandler::with_middleware(
|
||||||
|
@ -41,7 +41,7 @@ use miner::external::ExternalMiner;
|
|||||||
use node_filter::NodeFilter;
|
use node_filter::NodeFilter;
|
||||||
use node_health;
|
use node_health;
|
||||||
use parity_reactor::EventLoop;
|
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 updater::{UpdatePolicy, Updater};
|
||||||
use parity_version::version;
|
use parity_version::version;
|
||||||
use ethcore_private_tx::{ProviderConfig, EncryptorConfig, SecretStoreEncryptor};
|
use ethcore_private_tx::{ProviderConfig, EncryptorConfig, SecretStoreEncryptor};
|
||||||
@ -56,6 +56,7 @@ use cache::CacheConfig;
|
|||||||
use user_defaults::UserDefaults;
|
use user_defaults::UserDefaults;
|
||||||
use dapps;
|
use dapps;
|
||||||
use ipfs;
|
use ipfs;
|
||||||
|
use jsonrpc_core;
|
||||||
use modules;
|
use modules;
|
||||||
use rpc;
|
use rpc;
|
||||||
use rpc_apis;
|
use rpc_apis;
|
||||||
@ -369,6 +370,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
|
|||||||
};
|
};
|
||||||
|
|
||||||
// start rpc servers
|
// 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 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 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)?;
|
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
|
||||||
@ -390,6 +392,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
|
|||||||
|
|
||||||
Ok(RunningClient {
|
Ok(RunningClient {
|
||||||
inner: RunningClientInner::Light {
|
inner: RunningClientInner::Light {
|
||||||
|
rpc: rpc_direct,
|
||||||
informant,
|
informant,
|
||||||
client,
|
client,
|
||||||
keep_alive: Box::new((event_loop, service, ws_server, http_server, ipc_server, ui_server)),
|
keep_alive: Box::new((event_loop, service, ws_server, http_server, ipc_server, ui_server)),
|
||||||
@ -805,6 +808,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// start rpc servers
|
// 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 ws_server = rpc::new_ws(cmd.ws_conf.clone(), &dependencies)?;
|
||||||
let ipc_server = rpc::new_ipc(cmd.ipc_conf, &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)?;
|
let http_server = rpc::new_http("HTTP JSON-RPC", "jsonrpc", cmd.http_conf.clone(), &dependencies, dapps_middleware)?;
|
||||||
@ -878,6 +882,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
|
|
||||||
Ok(RunningClient {
|
Ok(RunningClient {
|
||||||
inner: RunningClientInner::Full {
|
inner: RunningClientInner::Full {
|
||||||
|
rpc: rpc_direct,
|
||||||
informant,
|
informant,
|
||||||
client,
|
client,
|
||||||
keep_alive: Box::new((watcher, service, updater, ws_server, http_server, ipc_server, ui_server, secretstore_key_server, ipfs_server, event_loop)),
|
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<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
/// Should be destroyed by calling `shutdown()`, otherwise execution will continue in the
|
/// Should be destroyed by calling `shutdown()`, otherwise execution will continue in the
|
||||||
/// background.
|
/// background.
|
||||||
pub struct RunningClient {
|
pub struct RunningClient {
|
||||||
inner: RunningClientInner
|
inner: RunningClientInner,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum RunningClientInner {
|
enum RunningClientInner {
|
||||||
Light {
|
Light {
|
||||||
|
rpc: jsonrpc_core::MetaIoHandler<Metadata, informant::Middleware<rpc_apis::LightClientNotifier>>,
|
||||||
informant: Arc<Informant<LightNodeInformantData>>,
|
informant: Arc<Informant<LightNodeInformantData>>,
|
||||||
client: Arc<LightClient>,
|
client: Arc<LightClient>,
|
||||||
keep_alive: Box<Any>,
|
keep_alive: Box<Any>,
|
||||||
},
|
},
|
||||||
Full {
|
Full {
|
||||||
|
rpc: jsonrpc_core::MetaIoHandler<Metadata, informant::Middleware<informant::ClientNotifier>>,
|
||||||
informant: Arc<Informant<FullNodeInformantData>>,
|
informant: Arc<Informant<FullNodeInformantData>>,
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
keep_alive: Box<Any>,
|
keep_alive: Box<Any>,
|
||||||
@ -907,25 +914,45 @@ enum RunningClientInner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl RunningClient {
|
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,
|
||||||
|
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.
|
/// Shuts down the client.
|
||||||
pub fn shutdown(self) {
|
pub fn shutdown(self) {
|
||||||
match self.inner {
|
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
|
// Create a weak reference to the client so that we can wait on shutdown
|
||||||
// until it is dropped
|
// until it is dropped
|
||||||
let weak_client = Arc::downgrade(&client);
|
let weak_client = Arc::downgrade(&client);
|
||||||
|
drop(rpc);
|
||||||
drop(keep_alive);
|
drop(keep_alive);
|
||||||
informant.shutdown();
|
informant.shutdown();
|
||||||
drop(informant);
|
drop(informant);
|
||||||
drop(client);
|
drop(client);
|
||||||
wait_for_drop(weak_client);
|
wait_for_drop(weak_client);
|
||||||
},
|
},
|
||||||
RunningClientInner::Full { informant, client, keep_alive } => {
|
RunningClientInner::Full { rpc, informant, client, keep_alive } => {
|
||||||
info!("Finishing work, please wait...");
|
info!("Finishing work, please wait...");
|
||||||
// Create a weak reference to the client so that we can wait on shutdown
|
// Create a weak reference to the client so that we can wait on shutdown
|
||||||
// until it is dropped
|
// until it is dropped
|
||||||
let weak_client = Arc::downgrade(&client);
|
let weak_client = Arc::downgrade(&client);
|
||||||
// drop this stuff as soon as exit detected.
|
// drop this stuff as soon as exit detected.
|
||||||
|
drop(rpc);
|
||||||
drop(keep_alive);
|
drop(keep_alive);
|
||||||
// to make sure timer does not spawn requests while shutdown is in progress
|
// to make sure timer does not spawn requests while shutdown is in progress
|
||||||
informant.shutdown();
|
informant.shutdown();
|
||||||
|
@ -49,6 +49,9 @@ pub enum Origin {
|
|||||||
/// Session id
|
/// Session id
|
||||||
session: H256
|
session: H256
|
||||||
},
|
},
|
||||||
|
/// From the C API
|
||||||
|
#[serde(rename="c-api")]
|
||||||
|
CApi,
|
||||||
/// Unknown
|
/// Unknown
|
||||||
#[serde(rename="unknown")]
|
#[serde(rename="unknown")]
|
||||||
Unknown,
|
Unknown,
|
||||||
@ -68,6 +71,7 @@ impl fmt::Display for Origin {
|
|||||||
Origin::Ipc(ref session) => write!(f, "IPC (session: {})", session),
|
Origin::Ipc(ref session) => write!(f, "IPC (session: {})", session),
|
||||||
Origin::Ws { ref session, ref dapp } => write!(f, "{} via WebSocket (session: {})", dapp, 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::Signer { ref session, ref dapp } => write!(f, "{} via UI (session: {})", dapp, session),
|
||||||
|
Origin::CApi => write!(f, "C API"),
|
||||||
Origin::Unknown => write!(f, "unknown origin"),
|
Origin::Unknown => write!(f, "unknown origin"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user