diff --git a/rust-evmjit/Cargo.toml b/rust-evmjit/Cargo.toml index 54c4437f4..4fabe4f97 100644 --- a/rust-evmjit/Cargo.toml +++ b/rust-evmjit/Cargo.toml @@ -5,3 +5,4 @@ authors = ["debris "] [dependencies] libc = "0.2.2" +tiny-keccak = "1.0" diff --git a/rust-evmjit/src/lib.rs b/rust-evmjit/src/lib.rs index b7742e5fb..8958f2b2f 100644 --- a/rust-evmjit/src/lib.rs +++ b/rust-evmjit/src/lib.rs @@ -1,67 +1,310 @@ -//! Bare rust wrapper around evmjit +//! Bare rust wrapper around evmjit. //! //! Requires latest version of Ethereum EVM JIT. https://github.com/debris/evmjit +//! +//! ``` +//! extern crate evmjit; +//! use evmjit::*; +//! +//! fn main() { +//! let mut context = ContextHandle::new(RuntimeDataHandle::new(), EnvHandle::empty()); +//! assert_eq!(context.exec(), ReturnCode::Stop); +//! } +//! +//! ``` extern crate libc; -use std::ptr; +extern crate tiny_keccak; -#[repr(C)] -pub struct JitI256 { - pub words: [u64; 4] +use std::ops::{Deref, DerefMut}; +use self::ffi::*; + +pub use self::ffi::JitReturnCode as ReturnCode; + +/// Component oriented safe handle to `JitRuntimeData`. +pub struct RuntimeDataHandle { + runtime_data: *mut JitRuntimeData } -#[repr(C)] -pub struct JitRuntimeData { - pub gas: i64, - pub gas_price: i64, - pub call_data: *const libc::c_char, - pub call_data_size: u64, - pub address: JitI256, - pub caller: JitI256, - pub origin: JitI256, - pub call_value: JitI256, - pub coinbase: JitI256, - pub difficulty: JitI256, - pub gas_limit: JitI256, - pub number: u64, - pub timestamp: i64, - pub code: *const libc::c_char, - pub code_size: u64, - pub code_hash: JitI256 +impl RuntimeDataHandle { + /// Creates new handle. + pub fn new() -> Self { + RuntimeDataHandle { + runtime_data: unsafe { evmjit_create_runtime_data() } + } + } + + /// Returns immutable reference to runtime data. + pub fn runtime_data(&self) -> &JitRuntimeData { + unsafe { &*self.runtime_data } + } + + /// Returns mutable reference to runtime data. + pub fn mut_runtime_data(&mut self) -> &mut JitRuntimeData { + unsafe { &mut *self.runtime_data } + } } -#[repr(C)] -#[derive(Debug, Eq, PartialEq)] -pub enum JitReturnCode { - Stop = 0, - Return = 1, - Suicide = 2, - - OutOfGas = -1, - - LLVMError = -101, - UnexpectedError = -111 +impl Drop for RuntimeDataHandle { + fn drop(&mut self) { + unsafe { evmjit_destroy_runtime_data(self.runtime_data) } + } } -pub enum JitContext {} +/// Safe handle for jit context. +pub struct ContextHandle { + context: *mut JitContext, + _data_handle: RuntimeDataHandle +} -#[link(name="evmjit")] -extern "C" { - pub fn evmjit_create_runtime_data() -> *mut JitRuntimeData; - pub fn evmjit_destroy_runtime_data(data: *mut JitRuntimeData); +impl ContextHandle { + /// Creates new context handle. + pub fn new(mut data_handle: RuntimeDataHandle, mut env: EnvHandle) -> Self { + let context = unsafe { evmjit_create_context(data_handle.mut_runtime_data(), &mut env) }; + ContextHandle { + context: context, + _data_handle: data_handle + } + } - pub fn evmjit_create_context(data: *mut JitRuntimeData, env: u8) -> *mut JitContext; - pub fn evmjit_destroy_context(context: *mut JitContext); + /// Executes context. + pub fn exec(&mut self) -> JitReturnCode { + unsafe { evmjit_exec(self.context) } + } +} - pub fn evmjit_exec(context: *mut JitContext) -> JitReturnCode; +impl Drop for ContextHandle { + fn drop(&mut self) { + unsafe { evmjit_destroy_context(self.context); } + } +} +/// Component oriented wrapper around jit env c interface. +pub trait Env { + fn sload(&self, index: *const JitI256, out_value: *mut JitI256); + fn sstore(&mut self, index: *const JitI256, value: *const JitI256); + fn balance(&self, address: *const JitI256, out_value: *mut JitI256); + fn blockhash(&self, number: *const JitI256, out_hash: *mut JitI256); + + fn create(&mut self, + io_gas: *mut u64, + endowment: *const JitI256, + init_beg: *const u8, + init_size: *const u64, + address: *mut JitI256); + + fn call(&mut self, + io_gas: *mut u64, + call_gas: *const u64, + receive_address: *const JitI256, + value: *const JitI256, + in_beg: *const u8, + in_size: *const u64, + out_beg: *mut u8, + out_size: *mut u64, + code_address: JitI256) -> bool; + + fn log(&mut self, + beg: *const u8, + size: *const u64, + topic1: *const JitI256, + topic2: *const JitI256, + topic3: *const JitI256, + topic4: *const JitI256); + + fn extcode(&self, address: *const JitI256, size: *mut u64) -> *const u8; +} + +/// C abi compatible wrapper for jit env implementers. +pub struct EnvHandle { + env_impl: Option> +} + +impl EnvHandle { + /// Creates new environment wrapper for given implementation + pub fn new(env_impl: T) -> Self where T: Env + 'static { + EnvHandle { env_impl: Some(Box::new(env_impl)) } + } + + /// Creates empty environment. + /// It can be used to for any operations. + pub fn empty() -> Self { + EnvHandle { env_impl: None } + } +} + +impl Deref for EnvHandle { + type Target = Box; + + fn deref(&self) -> &Self::Target { + match self.env_impl { + Some(ref env) => env, + None => { panic!(); } + } + } +} + +impl DerefMut for EnvHandle { + fn deref_mut(&mut self) -> &mut Self::Target { + match self.env_impl { + Some(ref mut env) => env, + None => { panic!(); } + } + } +} + +/// ffi functions +pub mod ffi { + use std::slice; + use libc; + use tiny_keccak::Keccak; + use super::*; + + /// Jit context struct declaration. + pub enum JitContext {} + + #[repr(C)] + #[derive(Debug, Eq, PartialEq)] + /// Jit context execution return code. + pub enum JitReturnCode { + Stop = 0, + Return = 1, + Suicide = 2, + + OutOfGas = -1, + + LLVMError = -101, + UnexpectedError = -111 + } + + #[repr(C)] + /// Signed 256 bit integer. + pub struct JitI256 { + pub words: [u64; 4] + } + + #[repr(C)] + /// Jit runtime data. + pub struct JitRuntimeData { + pub gas: i64, + pub gas_price: i64, + pub call_data: *const libc::c_char, + pub call_data_size: u64, + pub address: JitI256, + pub caller: JitI256, + pub origin: JitI256, + pub call_value: JitI256, + pub coinbase: JitI256, + pub difficulty: JitI256, + pub gas_limit: JitI256, + pub number: u64, + pub timestamp: i64, + pub code: *const libc::c_char, + pub code_size: u64, + pub code_hash: JitI256 + } + + #[no_mangle] + pub unsafe extern fn env_sload(env: *const EnvHandle, index: *const JitI256, out_value: *mut JitI256) { + let env = &*env; + env.sload(index, out_value); + } + + #[no_mangle] + pub unsafe extern fn env_sstore(env: *mut EnvHandle, index: *const JitI256, value: *const JitI256) { + let env = &mut *env; + env.sstore(index, value); + } + + #[no_mangle] + pub unsafe extern fn env_balance(env: *const EnvHandle, address: *const JitI256, out_value: *mut JitI256) { + let env = &*env; + env.balance(address, out_value); + } + + #[no_mangle] + pub unsafe extern fn env_blockhash(env: *const EnvHandle, number: *const JitI256, out_hash: *mut JitI256) { + let env = &*env; + env.blockhash(number, out_hash); + } + + #[no_mangle] + pub unsafe extern fn env_create(env: *mut EnvHandle, + io_gas: *mut u64, + endowment: *const JitI256, + init_beg: *const u8, + init_size: *const u64, + address: *mut JitI256) { + let env = &mut *env; + env.create(io_gas, endowment, init_beg, init_size, address); + } + + #[no_mangle] + pub unsafe extern fn env_call(env: *mut EnvHandle, + io_gas: *mut u64, + call_gas: *const u64, + receive_address: *const JitI256, + value: *const JitI256, + in_beg: *const u8, + in_size: *const u64, + out_beg: *mut u8, + out_size: *mut u64, + code_address: JitI256) -> bool { + let env = &mut *env; + env.call(io_gas, call_gas, receive_address, value, in_beg, in_size, out_beg, out_size, code_address) + } + + #[no_mangle] + pub unsafe extern fn env_sha3(begin: *const u8, size: *const u64, out_hash: *mut JitI256) { + let out_hash = &mut *out_hash; + let input = slice::from_raw_parts(begin, *size as usize); + let outlen = out_hash.words.len() * 8; + let output = slice::from_raw_parts_mut(out_hash.words.as_mut_ptr() as *mut u8, outlen); + let mut sha3 = Keccak::new_sha3_256(); + sha3.update(input); + sha3.finalize(output); + } + + #[no_mangle] + pub unsafe extern fn env_extcode(env: *const EnvHandle, address: *const JitI256, size: *mut u64) -> *const u8 { + let env = &*env; + env.extcode(address, size) + } + + #[no_mangle] + pub unsafe extern fn env_log(env: *mut EnvHandle, + beg: *const u8, + size: *const u64, + topic1: *const JitI256, + topic2: *const JitI256, + topic3: *const JitI256, + topic4: *const JitI256) { + let env = &mut *env; + env.log(beg, size, topic1, topic2, topic3, topic4); + } + + + #[link(name="evmjit")] + extern "C" { + pub fn evmjit_create_runtime_data() -> *mut JitRuntimeData; + pub fn evmjit_destroy_runtime_data(data: *mut JitRuntimeData); + pub fn evmjit_destroy_context(context: *mut JitContext); + pub fn evmjit_exec(context: *mut JitContext) -> JitReturnCode; + } + + #[link(name="evmjit")] + // EnvHandle does not have to by a C type + #[allow(improper_ctypes)] + extern "C" { + pub fn evmjit_create_context(data: *mut JitRuntimeData, env: *mut EnvHandle) -> *mut JitContext; + } } #[test] -fn it_works() { +fn ffi_test() { unsafe { let data = evmjit_create_runtime_data(); - let context = evmjit_create_context(data, 0); + let context = evmjit_create_context(data, &mut EnvHandle::empty()); let code = evmjit_exec(context); assert_eq!(code, JitReturnCode::Stop); @@ -71,3 +314,8 @@ fn it_works() { } } +#[test] +fn handle_test() { + let mut context = ContextHandle::new(RuntimeDataHandle::new(), EnvHandle::empty()); + assert_eq!(context.exec(), ReturnCode::Stop); +}