Skip to content

Commit

Permalink
Add high-level result interface
Browse files Browse the repository at this point in the history
  • Loading branch information
jakelang committed Mar 13, 2019
1 parent 2e69b5a commit 9c282e6
Show file tree
Hide file tree
Showing 2 changed files with 130 additions and 1 deletion.
1 change: 1 addition & 0 deletions .codespell-whitelist
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
iff
mut
130 changes: 129 additions & 1 deletion bindings/rust/evmc-vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,134 @@ pub extern crate evmc_sys;
pub use evmc_sys as ffi;

// TODO: Add helpers for host interface
// TODO: Add convenient helpers for evmc_result
// TODO: Add convenient helpers for evmc_execute
// TODO: Add a derive macro here for creating evmc_create

/// EVMC result structure.
pub struct EvmcResult(Box<ffi::evmc_result>);

impl EvmcResult {
/// Initialize a new result struct given a status code, remaining gas, and optional output
/// data.
pub fn new(status: ffi::evmc_status_code, gasleft: i64, output: Option<&[u8]>) -> Self {
EvmcResult(Box::new(ffi::evmc_result {
status_code: status,
gas_left: gasleft,
output_data: if let Some(buf) = output {
buf.clone().as_ptr()
} else {
std::ptr::null()
},
output_size: if let Some(buf) = output { buf.len() } else { 0 },
release: Some(release_result),
create_address: ffi::evmc_address { bytes: [0u8; 20] },
padding: [0u8; 4],
}))
}

/// Get gas left.
pub fn gasleft(&self) -> i64 {
self.0.gas_left
}

/// Get the address of the created contract. Does not return valid data if the transaction was
/// not a contract creation.
pub fn create_addr(&self) -> &ffi::evmc_address {
&self.0.create_address
}

/// Return the output buffer, if any.
pub fn output(&self) -> Option<&[u8]> {
if !self.0.output_data.is_null() && self.0.output_size != 0 {
Some(unsafe {
std::slice::from_raw_parts::<u8>(self.0.output_data, self.0.output_size)
})
} else {
None
}
}

/// Get the status code.
pub fn status(&self) -> ffi::evmc_status_code {
self.0.status_code
}

/// Consume the struct and unwrap to the contained box with the raw result struct.
pub fn into_inner(self) -> Box<ffi::evmc_result> {
self.0
}

/// Construct from a box containing the raw result struct.
pub fn from_boxed(inner: Box<ffi::evmc_result>) -> Self {
EvmcResult(inner)
}

/// Consume the struct and return a pointer to the raw result struct.
pub fn into_raw(self) -> *const ffi::evmc_result {
Box::into_raw(self.into_inner())
}

/// Construct from a pointer to the raw result struct.
pub fn from_raw(raw: *mut ffi::evmc_result) -> Self {
unsafe { EvmcResult::from_boxed(Box::from_raw(raw)) }
}
}

/// Callback to pass across FFI, de-allocating the struct.
extern "C" fn release_result(result: *const ffi::evmc_result) {
unsafe {
Box::from_raw(result as *mut ffi::evmc_result);
}
}

#[cfg(test)]
mod tests {
use super::*;
use evmc_sys as ffi;

#[test]
fn constructor() {
let result = EvmcResult::new(
ffi::evmc_status_code::EVMC_SUCCESS,
420,
Some(&[0xde, 0xad, 0xbe, 0xef]),
);
assert!(result.status() == ffi::evmc_status_code::EVMC_SUCCESS);
assert!(result.gasleft() == 420);
assert!(result.output().is_some());
assert!(result.output().unwrap().len() == 4);
assert!(result.output().unwrap() == [0xde, 0xad, 0xbe, 0xef]);
}

#[test]
fn inner() {
let result = EvmcResult::new(
ffi::evmc_status_code::EVMC_SUCCESS,
420,
Some(&[0xde, 0xad, 0xbe, 0xef]),
);
let inner = result.into_inner();
let back = EvmcResult::from_boxed(inner);
assert!(back.status() == ffi::evmc_status_code::EVMC_SUCCESS);
assert!(back.gasleft() == 420);
assert!(back.output().is_some());
assert!(back.output().unwrap().len() == 4);
assert!(back.output().unwrap() == [0xde, 0xad, 0xbe, 0xef]);
}

#[test]
fn raw() {
let result = EvmcResult::new(
ffi::evmc_status_code::EVMC_SUCCESS,
420,
Some(&[0xde, 0xad, 0xbe, 0xef]),
);
let raw = result.into_raw() as *mut ffi::evmc_result;
let back = EvmcResult::from_raw(raw);
assert!(back.status() == ffi::evmc_status_code::EVMC_SUCCESS);
assert!(back.gasleft() == 420);
assert!(back.output().is_some());
assert!(back.output().unwrap().len() == 4);
assert!(back.output().unwrap() == [0xde, 0xad, 0xbe, 0xef]);
}
}

0 comments on commit 9c282e6

Please sign in to comment.