mirror of https://github.com/status-im/evmc.git
rust: Implement ExecutionContext (host interface)
This commit is contained in:
parent
bc30e5a42f
commit
8d9d206ad0
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue