rust: Implement ExecutionContext (host interface)

This commit is contained in:
Jake Lang 2019-04-24 16:41:12 -04:00 committed by Alex Beregszaszi
parent bc30e5a42f
commit 8d9d206ad0
2 changed files with 277 additions and 3 deletions

View File

@ -6,7 +6,6 @@
pub extern crate evmc_sys;
pub use evmc_sys as ffi;
// TODO: Add helpers for host interface
// TODO: Add convenient helpers for evmc_execute
// TODO: Add a derive macro here for creating evmc_create
@ -18,6 +17,14 @@ pub struct ExecutionResult {
create_address: ffi::evmc_address,
}
/// EVMC context structure. Exposes the EVMC host functions, message data, and transaction context
/// to the executing VM.
pub struct ExecutionContext<'a> {
message: &'a ffi::evmc_message,
context: &'a mut ffi::evmc_context,
tx_context: ffi::evmc_tx_context,
}
impl ExecutionResult {
pub fn new(
_status_code: ffi::evmc_status_code,
@ -61,6 +68,172 @@ impl ExecutionResult {
}
}
impl<'a> ExecutionContext<'a> {
pub fn new(_message: &'a ffi::evmc_message, _context: &'a mut ffi::evmc_context) -> Self {
let _tx_context = unsafe {
assert!((*(_context.host)).get_tx_context.is_some());
(*(_context.host)).get_tx_context.unwrap()(_context as *mut ffi::evmc_context)
};
ExecutionContext {
message: _message,
context: _context,
tx_context: _tx_context,
}
}
pub fn get_message(&self) -> &ffi::evmc_message {
&self.message
}
pub fn get_tx_context(&mut self) -> &ffi::evmc_tx_context {
&self.tx_context
}
pub fn account_exists(&mut self, address: &ffi::evmc_address) -> bool {
unsafe {
assert!((*self.context.host).account_exists.is_some());
(*self.context.host).account_exists.unwrap()(
self.context as *mut ffi::evmc_context,
address as *const ffi::evmc_address,
)
}
}
pub fn get_storage(
&mut self,
address: &ffi::evmc_address,
key: &ffi::evmc_bytes32,
) -> ffi::evmc_bytes32 {
unsafe {
assert!((*self.context.host).get_storage.is_some());
(*self.context.host).get_storage.unwrap()(
self.context as *mut ffi::evmc_context,
address as *const ffi::evmc_address,
key as *const ffi::evmc_bytes32,
)
}
}
pub fn set_storage(
&mut self,
address: &ffi::evmc_address,
key: &ffi::evmc_bytes32,
value: &ffi::evmc_bytes32,
) -> ffi::evmc_storage_status {
unsafe {
assert!((*self.context.host).set_storage.is_some());
(*self.context.host).set_storage.unwrap()(
self.context as *mut ffi::evmc_context,
address as *const ffi::evmc_address,
key as *const ffi::evmc_bytes32,
value as *const ffi::evmc_bytes32,
)
}
}
pub fn get_balance(&mut self, address: &ffi::evmc_address) -> ffi::evmc_bytes32 {
unsafe {
assert!((*self.context.host).get_balance.is_some());
(*self.context.host).get_balance.unwrap()(
self.context as *mut ffi::evmc_context,
address as *const ffi::evmc_address,
)
}
}
pub fn get_code_size(&mut self, address: &ffi::evmc_address) -> usize {
unsafe {
assert!((*self.context.host).get_code_size.is_some());
(*self.context.host).get_code_size.unwrap()(
self.context as *mut ffi::evmc_context,
address as *const ffi::evmc_address,
)
}
}
pub fn get_code_hash(&mut self, address: &ffi::evmc_address) -> ffi::evmc_bytes32 {
unsafe {
assert!((*self.context.host).get_code_size.is_some());
(*self.context.host).get_code_hash.unwrap()(
self.context as *mut ffi::evmc_context,
address as *const ffi::evmc_address,
)
}
}
pub fn copy_code(
&mut self,
address: &ffi::evmc_address,
code_offset: usize,
buffer: &mut [u8],
) -> usize {
unsafe {
assert!((*self.context.host).copy_code.is_some());
(*self.context.host).copy_code.unwrap()(
self.context as *mut ffi::evmc_context,
address as *const ffi::evmc_address,
code_offset,
// FIXME: ensure that alignment of the array elements is OK
buffer.as_mut_ptr(),
buffer.len(),
)
}
}
pub fn selfdestruct(&mut self, address: &ffi::evmc_address, beneficiary: &ffi::evmc_address) {
unsafe {
assert!((*self.context.host).selfdestruct.is_some());
(*self.context.host).selfdestruct.unwrap()(
self.context as *mut ffi::evmc_context,
address as *const ffi::evmc_address,
beneficiary as *const ffi::evmc_address,
)
}
}
pub fn call(&mut self, message: &ffi::evmc_message) -> ExecutionResult {
unsafe {
assert!((*self.context.host).call.is_some());
(*self.context.host).call.unwrap()(
self.context as *mut ffi::evmc_context,
message as *const ffi::evmc_message,
)
.into()
}
}
pub fn get_block_hash(&mut self, num: i64) -> ffi::evmc_bytes32 {
unsafe {
assert!((*self.context.host).get_block_hash.is_some());
(*self.context.host).get_block_hash.unwrap()(
self.context as *mut ffi::evmc_context,
num,
)
}
}
pub fn emit_log(
&mut self,
address: &ffi::evmc_address,
data: &[u8],
topics: &[ffi::evmc_bytes32],
) {
unsafe {
assert!((*self.context.host).emit_log.is_some());
(*self.context.host).emit_log.unwrap()(
self.context as *mut ffi::evmc_context,
address as *const ffi::evmc_address,
// FIXME: ensure that alignment of the array elements is OK
data.as_ptr(),
data.len(),
topics.as_ptr(),
topics.len(),
)
}
}
}
impl From<ffi::evmc_result> for ExecutionResult {
fn from(result: ffi::evmc_result) -> Self {
let ret = ExecutionResult {
@ -308,4 +481,99 @@ mod tests {
}
}
}
unsafe extern "C" fn get_dummy_tx_context(
_context: *mut ffi::evmc_context,
) -> ffi::evmc_tx_context {
ffi::evmc_tx_context {
tx_gas_price: ffi::evmc_uint256be { bytes: [0u8; 32] },
tx_origin: ffi::evmc_address { bytes: [0u8; 20] },
block_coinbase: ffi::evmc_address { bytes: [0u8; 20] },
block_number: 42,
block_timestamp: 235117,
block_gas_limit: 105023,
block_difficulty: ffi::evmc_uint256be { bytes: [0xaa; 32] },
}
}
unsafe extern "C" fn get_dummy_code_size(
_context: *mut ffi::evmc_context,
_addr: *const ffi::evmc_address,
) -> usize {
105023 as usize
}
// Update these when needed for tests
fn get_dummy_context() -> ffi::evmc_context {
ffi::evmc_context {
host: Box::into_raw(Box::new(ffi::evmc_host_interface {
account_exists: None,
get_storage: None,
set_storage: None,
get_balance: None,
get_code_size: Some(get_dummy_code_size),
get_code_hash: None,
copy_code: None,
selfdestruct: None,
call: None,
get_tx_context: Some(get_dummy_tx_context),
get_block_hash: None,
emit_log: None,
})),
}
}
fn get_dummy_message() -> ffi::evmc_message {
ffi::evmc_message {
kind: ffi::evmc_call_kind::EVMC_CALL,
flags: 0,
depth: 123,
gas: 105023,
destination: ffi::evmc_address { bytes: [0u8; 20] },
sender: ffi::evmc_address { bytes: [0u8; 20] },
input_data: std::ptr::null() as *const u8,
input_size: 0,
value: ffi::evmc_uint256be { bytes: [0u8; 32] },
create2_salt: ffi::evmc_uint256be { bytes: [0u8; 32] },
}
}
#[test]
fn execution_context() {
let msg = get_dummy_message();
let mut context_raw = get_dummy_context();
let mut exe_context = ExecutionContext::new(&msg, &mut context_raw);
let a = exe_context.get_tx_context();
let b = unsafe { get_dummy_tx_context(&mut get_dummy_context() as *mut ffi::evmc_context) };
assert_eq!(a.block_gas_limit, b.block_gas_limit);
assert_eq!(a.block_timestamp, b.block_timestamp);
assert_eq!(a.block_number, b.block_number);
let c = exe_context.get_message();
let d = get_dummy_message();
assert_eq!(c.kind, d.kind);
assert_eq!(c.flags, d.flags);
assert_eq!(c.depth, d.depth);
assert_eq!(c.gas, d.gas);
assert_eq!(c.input_data, d.input_data);
assert_eq!(c.input_size, d.input_size);
}
#[test]
fn get_code_size() {
let msg = get_dummy_message();
// This address is useless. Just a dummy parameter for the interface function.
let test_addr = ffi::evmc_address { bytes: [0u8; 20] };
let mut context_raw = get_dummy_context();
let mut exe_context = ExecutionContext::new(&msg, &mut context_raw);
let a: usize = 105023;
let b = exe_context.get_code_size(&test_addr);
assert_eq!(a, b);
}
}

View File

@ -1,6 +1,6 @@
extern crate evmc_vm;
use evmc_vm::evmc_sys as ffi;
use evmc_vm::*;
extern "C" fn execute(
instance: *mut ffi::evmc_instance,
@ -10,7 +10,13 @@ extern "C" fn execute(
code: *const u8,
code_size: usize,
) -> ffi::evmc_result {
let is_create = unsafe { (*msg).kind == ffi::evmc_call_kind::EVMC_CREATE };
let execution_ctx = unsafe {
ExecutionContext::new(
msg.as_ref().expect("tester passed nullptr as message"),
context.as_mut().expect("tester passed nullptr as context"),
)
};
let is_create = execution_ctx.get_message().kind == ffi::evmc_call_kind::EVMC_CREATE;
if is_create {
evmc_vm::ExecutionResult::new(ffi::evmc_status_code::EVMC_FAILURE, 0, None, None).into()