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

Commit

Permalink
feat(parity-clib asynchronous rpc queries)
Browse files Browse the repository at this point in the history
  • Loading branch information
niklasad1 committed Nov 14, 2018
1 parent ac974a1 commit 86ff729
Show file tree
Hide file tree
Showing 10 changed files with 293 additions and 171 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

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

1 change: 1 addition & 0 deletions logger/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ pub fn setup_log(config: &Config) -> Result<Arc<RotatingLogger>, String> {
builder.filter(Some("ws"), LevelFilter::Warn);
builder.filter(Some("hyper"), LevelFilter::Warn);
builder.filter(Some("rustls"), LevelFilter::Error);
builder.filter(Some("rpc"), LevelFilter::Trace);
// Enable info for others.
builder.filter(None, LevelFilter::Info);

Expand Down
134 changes: 105 additions & 29 deletions parity-clib-examples/cpp/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,44 +14,120 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.

// Note, `nullptr` requires a C++ compiler with C+11 support

#include <cstddef>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <parity.h>

int parity_light();
int parity_full();

// global variable to keep track of the received rpc responses
static int g_num_queries = 0;

// list of rpc queries
static const char* rpc_queries[] = {
"{\"method\":\"parity_versionInfo\",\"params\":[],\"id\":1,\"jsonrpc\":\"2.0\"}",
"{\"method\":\"eth_getTransactionReceipt\",\"params\":[\"0x444172bef57ad978655171a8af2cfd89baa02a97fcb773067aef7794d6913fff\"],\"id\":1,\"jsonrpc\":\"2.0\"}",
"{\"method\":\"eth_estimateGas\",\"params\":[{\"from\":\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"}],\"id\":1,\"jsonrpc\":\"2.0\"}",
"{\"method\":\"eth_getBalance\",\"params\":[\"0x0066Dc48bb833d2B59f730F33952B3c29fE926F5\"],\"id\":1,\"jsonrpc\":\"2.0\"}"
};

// callback that gets invoked when the client restarts
void on_restart(void*, const char*, size_t) {}

int main() {
ParityParams cfg = { 0 };
cfg.on_client_restart_cb = on_restart;

const char* args[] = {"--no-ipc"};
size_t str_lens[] = {8};
if (parity_config_from_cli(args, str_lens, 1, &cfg.configuration) != 0) {
return 1;
}

void* parity;
if (parity_start(&cfg, &parity) != 0) {
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);
}
// callback that is invoked on rpc responses
void rpc_response(void *, const char* response, size_t len) {
printf("rpc_callback: %s \r\n", response);
g_num_queries -= 1;
}

int main() {
// run the list of queries in the light client
if (parity_light() != 0) {
printf("parity light client failed\r\n");
}
// run the list of queries in the full client
if (parity_full() != 0) {
printf("parity client failed\r\n");
}
return 0;
}

int parity_light() {
int num_queries = sizeof(rpc_queries) / sizeof(rpc_queries[0]);
g_num_queries = num_queries;

ParityParams cfg = {
.configuration = nullptr,
.on_client_restart_cb = on_restart,
.on_client_restart_cb_custom = nullptr
};

const char* args[] = {"--light", "--no-ipc"};
size_t str_lens[] = {strlen(args[0]), strlen(args[1])};

if (parity_config_from_cli(args, str_lens, sizeof(str_lens)/sizeof(str_lens[0]), &cfg.configuration) != 0) {
return 1;
}

void *parity = nullptr;
if (parity_start(&cfg, &parity) != 0) {
return 1;
}

for (int i = 0; i < num_queries; i++) {
if (parity_rpc(parity, rpc_queries[i], strlen(rpc_queries[i]), rpc_response) != 0) {
return 1;
}
}

// wait until all queries have been answered
while(g_num_queries != 0);

if (parity != NULL) {
parity_destroy(parity);
}
return 0;
}

int parity_full() {
int num_queries = sizeof(rpc_queries) / sizeof(rpc_queries[0]);
g_num_queries = num_queries;

ParityParams cfg = {
.configuration = nullptr,
.on_client_restart_cb = on_restart,
.on_client_restart_cb_custom = nullptr
};

const char* args[] = {"--no-ipc"};
size_t str_lens[] = {strlen(args[0])};

if (parity_config_from_cli(args, str_lens, sizeof(str_lens)/sizeof(str_lens[0]), &cfg.configuration) != 0) {
return 1;
}

void *parity = nullptr;
if (parity_start(&cfg, &parity) != 0) {
return 1;
}

for (int i = 0; i < num_queries; i++) {
if (parity_rpc(parity, rpc_queries[i], strlen(rpc_queries[i]), rpc_response) != 0) {
return 1;
}
}

// wait until all queries have been answered
while(g_num_queries != 0);

if (parity != NULL) {
parity_destroy(parity);
}
return 0;
}
5 changes: 4 additions & 1 deletion parity-clib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ name = "parity"
crate-type = ["cdylib", "staticlib"]

[dependencies]
futures = "0.1.6"
jni = { version = "0.10.1", optional = true }
panic_hook = { path = "../util/panic_hook" }
parity-ethereum = { path = "../", default-features = false }
jni = { version = "0.10.1", optional = true }
tokio = "0.1.11"
tokio-current-thread = "0.1.3"

[features]
default = []
Expand Down
21 changes: 7 additions & 14 deletions parity-clib/parity.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@

#include <stddef.h>

typedef void (subscribe)(void*, const char*, size_t);

/// Parameters to pass to `parity_start`.
struct ParityParams {
/// Configuration object, as handled by the `parity_config_*` functions.
Expand All @@ -31,7 +33,7 @@ struct ParityParams {
///
/// The first parameter of the callback is the value of `on_client_restart_cb_custom`.
/// The second and third parameters of the callback are the string pointer and length.
void (*on_client_restart_cb)(void* custom, const char* new_chain, size_t new_chain_len);
subscribe *on_client_restart_cb;

/// Custom parameter passed to the `on_client_restart_cb` callback as first parameter.
void *on_client_restart_cb_custom;
Expand Down Expand Up @@ -86,21 +88,12 @@ 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.
/// Performs an asynchronous RPC request.
///
/// - `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.
/// - On success, the function returns a callback with the result in `null` terminated `cstring`
///
int parity_rpc(void* parity, const char* rpc, size_t len, char* out_str, size_t* out_len);
int parity_rpc(void* parity, const char* rpc_query, size_t len, subscribe rpc_response);

/// Sets a callback to call when a panic happens in the Rust code.
///
Expand All @@ -117,7 +110,7 @@ int parity_rpc(void* parity, const char* rpc, size_t len, char* out_str, size_t*
/// The callback can be called from any thread and multiple times simultaneously. Make sure that
/// your code is thread safe.
///
int parity_set_panic_hook(void (*cb)(void* param, const char* msg, size_t msg_len), void* param);
int parity_set_panic_hook(subscribe panic, void* param);

#ifdef __cplusplus
}
Expand Down
91 changes: 91 additions & 0 deletions parity-clib/src/java.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use std::{mem, ptr};
use std::os::raw::c_void;

use {Callback, parity_config_from_cli, parity_destroy, parity_start, parity_rpc, ParityParams};
use jni::{JNIEnv, objects::JClass, objects::JString, sys::jlong, sys::jobjectArray};

#[no_mangle]
pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_configFromCli(env: JNIEnv, _: JClass, cli: jobjectArray) -> jlong {
let cli_len = env.get_array_length(cli).expect("invalid Java bindings");

let mut jni_strings = Vec::with_capacity(cli_len as usize);
let mut opts = Vec::with_capacity(cli_len as usize);
let mut opts_lens = Vec::with_capacity(cli_len as usize);

for n in 0 .. cli_len {
let elem = env.get_object_array_element(cli, n).expect("invalid Java bindings");
let elem_str: JString = elem.into();
match env.get_string(elem_str) {
Ok(s) => {
opts.push(s.as_ptr());
opts_lens.push(s.to_bytes().len());
jni_strings.push(s);
},
Err(err) => {
let _ = env.throw_new("java/lang/Exception", err.to_string());
return 0
}
};
}

let mut out = ptr::null_mut();
match parity_config_from_cli(opts.as_ptr(), opts_lens.as_ptr(), cli_len as usize, &mut out) {
0 => out as usize as jlong,
_ => {
let _ = env.throw_new("java/lang/Exception", "failed to create config object");
0
},
}
}

#[no_mangle]
pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_build(env: JNIEnv, _: JClass, config: jlong) -> jlong {
let params = ParityParams {
configuration: config as usize as *mut c_void,
.. mem::zeroed()
};

let mut out = ptr::null_mut();
match parity_start(&params, &mut out) {
0 => out as usize as jlong,
_ => {
let _ = env.throw_new("java/lang/Exception", "failed to start Parity");
0
},
}
}

#[no_mangle]
pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_destroy(_env: JNIEnv, _: JClass, parity: jlong) {
let parity = parity as usize as *mut c_void;
parity_destroy(parity);
}

#[no_mangle]
pub unsafe extern "system" fn Java_io_parity_ethereum_Parity_rpcQueryNative<'a>(
env: JNIEnv<'a>,
_: JClass,
parity:jlong,
rpc: JString,
callback: Callback,
)
{
let parity = parity as usize as *mut c_void;

let rpc = match env.get_string(rpc) {
Ok(s) => s,
Err(err) => {
let _ = env.throw_new("java/lang/Exception", err.to_string());
return;
},
};

//FIXME: provide "callback" in java fashion
match parity_rpc(parity, rpc.as_ptr(), rpc.to_bytes().len(), callback) {
0 => (),
_ => {
let _ = env.throw_new("java/lang/Exception", "failed to perform RPC query");
return;
},
}
}
Loading

0 comments on commit 86ff729

Please sign in to comment.