diff --git a/Cargo.toml b/Cargo.toml index 287f70a..562c9a5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,11 @@ edition = "2018" [dependencies] c_vec = "1.3.3" +ethcore-builtin = { path = "../parity-ethereum/ethcore/builtin" } +lazy_static = "1.3.0" libc = "0.2.60" parity-bytes = "0.1.0" -ethcore-builtin = { path = "../parity-ethereum/ethcore/builtin" } +rustler = "0.20.0" [lib] crate-type = ["cdylib"] \ No newline at end of file diff --git a/Makefile b/Makefile index 54e8e6a..4711c25 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ DEBUG ?= 0 ifeq ($(DEBUG), 1) CFLAGS =-DDEBUG=1 else - CFLAGS=-DNDEBUG + CFLAGS=-DNDEBUG -Ofast endif CC = gcc @@ -15,7 +15,7 @@ INCLUDEPATH = -Iinclude all: test erl_ecrecover -test: src/test.c src/base64.c target/debug/libecrecover.so +test: src/test.c src/base64.c target/release/libecrecover.so $(CC) -o $@ $^ $(INCLUDEPATH) $(CFLAGS) $(LDPATH) ./test diff --git a/src/ecrecover.erl b/src/ecrecover.erl index 91f324e..e2e277f 100644 --- a/src/ecrecover.erl +++ b/src/ecrecover.erl @@ -1,6 +1,11 @@ -module(ecrecover). -export([start/1, stop/0, init/1]). -export([ecrecover/1]). +-export([time_taken_to_execute/1]). + +time_taken_to_execute(F) -> Start = os:timestamp(), + F(), + io:format("total time taken ~f seconds~n", [timer:now_diff(os:timestamp(), Start) / 1000000]). start(ExtPrg) -> spawn(?MODULE, init, [ExtPrg]). diff --git a/src/lib.rs b/src/lib.rs index f65f203..0d297de 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,72 @@ extern crate c_vec; extern crate ethcore_builtin; +#[macro_use] +extern crate lazy_static; extern crate libc; extern crate parity_bytes; +#[macro_use] +extern crate rustler; use std::ffi::*; use ethcore_builtin::EcRecover; use crate::ethcore_builtin::Implementation; use c_vec::{CVec}; use parity_bytes::BytesRef; +use rustler::*; use std::ptr::copy_nonoverlapping; const INPUT_LENGTH: usize = 512; const OUTPUT_LENGTH: usize = 32; +mod atoms { + rustler_atoms! { + atom ok; + } +} + +rustler_export_nifs!( + "nifecrecover", + [ + ("ecrecover", 2, nif_ecrecover), + ], + Some(on_load) +); + +struct EcrecoverResource { } + +#[no_mangle] +fn on_load(env: Env, _load_info: Term) -> bool { + println!("on_load"); + rustler::resource_struct_init!(EcrecoverResource, env); + true +} + +pub fn nif_ecrecover<'a>(env: Env<'a>, args: &[Term<'a>]) -> Result, Error> { + let input: String = args[0].decode()?; + let mut output: String = args[1].decode()?; + let mut byte_ref = Vec::new(); + let ecrecover = EcRecover { }; + let result = match ecrecover.execute(input.as_bytes(), + &mut BytesRef::Flexible(&mut byte_ref)) { + Ok(x) => x, + Err(e) => return Err(rustler::Error::Atom("ecrecover failed")), + }; + match String::from_utf8(byte_ref) { + Ok(x) => { + output.push_str(&x); + Ok((atoms::ok(), true).encode(env)) + }, + Err(x) => Err(rustler::Error::Atom("Invalid UTF-8")), + } +} + +/** + * C interface to the ethereum ecrecover implementation. Returns 1 on success, + * 0 on failure (in which case there are no guarantees as to what is in output. + * + * Memory allocated by caller, expected to be an array of bytes, as above-- 512 in, + * 32 out. + */ #[no_mangle] pub unsafe extern "C" fn ecrecover(input: *const libc::c_uchar, output: *mut libc::c_uchar) -> i16 { let ecrecover = EcRecover { }; @@ -27,6 +81,8 @@ pub unsafe extern "C" fn ecrecover(input: *const libc::c_uchar, output: *mut lib return 1; } + + #[cfg(test)] mod tests { #[test] diff --git a/src/nifecrecover.erl b/src/nifecrecover.erl new file mode 100644 index 0000000..c2444ad --- /dev/null +++ b/src/nifecrecover.erl @@ -0,0 +1,27 @@ +-module(nifecrecover). + +%% API +-export([ecrecover/2]). + +%% Native library support +-export([load/0]). +-on_load(load/0). + +ecrecover(_Input, _Output) -> + not_loaded(?LINE). + +load() -> + ok = erlang:load_nif("/home/newby/projects/ethereum/ecrecover/target/debug/libecrecover", 0). + +not_loaded(Line) -> + erlang:nif_error({error, {not_loaded, [{module, ?MODULE}, {line, Line}]}}). + +priv()-> + case code:priv_dir(?MODULE) of + {error, _} -> + EbinDir = filename:dirname(code:which(?MODULE)), + AppPath = filename:dirname(EbinDir), + filename:join(AppPath, "priv"); + Path -> + Path + end.