mod container;
mod types;
pub use container::EvmcContainer;
pub use evmc_sys as ffi;
pub use types::*;
pub trait EvmcVm {
fn init() -> Self;
fn execute<'a>(
&self,
revision: Revision,
code: &'a [u8],
message: &'a ExecutionMessage,
context: Option<&'a mut ExecutionContext<'a>>,
) -> ExecutionResult;
}
#[derive(Debug)]
pub struct ExecutionResult {
status_code: StatusCode,
gas_left: i64,
output: Option<Vec<u8>>,
create_address: Option<Address>,
}
#[derive(Debug)]
pub struct ExecutionMessage {
kind: MessageKind,
flags: u32,
depth: i32,
gas: i64,
destination: Address,
sender: Address,
input: Option<Vec<u8>>,
value: Uint256,
create2_salt: Bytes32,
}
pub type ExecutionTxContext = ffi::evmc_tx_context;
pub struct ExecutionContext<'a> {
host: &'a ffi::evmc_host_interface,
context: *mut ffi::evmc_host_context,
tx_context: ExecutionTxContext,
}
impl ExecutionResult {
pub fn new(_status_code: StatusCode, _gas_left: i64, _output: Option<&[u8]>) -> Self {
ExecutionResult {
status_code: _status_code,
gas_left: _gas_left,
output: if let Some(output) = _output {
Some(output.to_vec())
} else {
None
},
create_address: None,
}
}
pub fn failure() -> Self {
ExecutionResult::new(StatusCode::EVMC_FAILURE, 0, None)
}
pub fn revert(_gas_left: i64, _output: Option<&[u8]>) -> Self {
ExecutionResult::new(StatusCode::EVMC_REVERT, _gas_left, _output)
}
pub fn success(_gas_left: i64, _output: Option<&[u8]>) -> Self {
ExecutionResult::new(StatusCode::EVMC_SUCCESS, _gas_left, _output)
}
pub fn status_code(&self) -> StatusCode {
self.status_code
}
pub fn gas_left(&self) -> i64 {
self.gas_left
}
pub fn output(&self) -> Option<&Vec<u8>> {
self.output.as_ref()
}
pub fn create_address(&self) -> Option<&Address> {
self.create_address.as_ref()
}
}
impl ExecutionMessage {
pub fn new(
kind: MessageKind,
flags: u32,
depth: i32,
gas: i64,
destination: Address,
sender: Address,
input: Option<&[u8]>,
value: Uint256,
create2_salt: Bytes32,
) -> Self {
ExecutionMessage {
kind,
flags,
depth,
gas,
destination,
sender,
input: if let Some(input) = input {
Some(input.to_vec())
} else {
None
},
value,
create2_salt,
}
}
pub fn kind(&self) -> MessageKind {
self.kind
}
pub fn flags(&self) -> u32 {
self.flags
}
pub fn depth(&self) -> i32 {
self.depth
}
pub fn gas(&self) -> i64 {
self.gas
}
pub fn destination(&self) -> &Address {
&self.destination
}
pub fn sender(&self) -> &Address {
&self.sender
}
pub fn input(&self) -> Option<&Vec<u8>> {
self.input.as_ref()
}
pub fn value(&self) -> &Uint256 {
&self.value
}
pub fn create2_salt(&self) -> &Bytes32 {
&self.create2_salt
}
}
impl<'a> ExecutionContext<'a> {
pub fn new(host: &'a ffi::evmc_host_interface, _context: *mut ffi::evmc_host_context) -> Self {
let _tx_context = unsafe {
assert!((*host).get_tx_context.is_some());
(*host).get_tx_context.unwrap()(_context)
};
ExecutionContext {
host: host,
context: _context,
tx_context: _tx_context,
}
}
pub fn get_tx_context(&self) -> &ExecutionTxContext {
&self.tx_context
}
pub fn account_exists(&self, address: &Address) -> bool {
unsafe {
assert!((*self.host).account_exists.is_some());
(*self.host).account_exists.unwrap()(self.context, address as *const Address)
}
}
pub fn get_storage(&self, address: &Address, key: &Bytes32) -> Bytes32 {
unsafe {
assert!((*self.host).get_storage.is_some());
(*self.host).get_storage.unwrap()(
self.context,
address as *const Address,
key as *const Bytes32,
)
}
}
pub fn set_storage(
&mut self,
address: &Address,
key: &Bytes32,
value: &Bytes32,
) -> StorageStatus {
unsafe {
assert!((*self.host).set_storage.is_some());
(*self.host).set_storage.unwrap()(
self.context,
address as *const Address,
key as *const Bytes32,
value as *const Bytes32,
)
}
}
pub fn get_balance(&self, address: &Address) -> Uint256 {
unsafe {
assert!((*self.host).get_balance.is_some());
(*self.host).get_balance.unwrap()(self.context, address as *const Address)
}
}
pub fn get_code_size(&self, address: &Address) -> usize {
unsafe {
assert!((*self.host).get_code_size.is_some());
(*self.host).get_code_size.unwrap()(self.context, address as *const Address)
}
}
pub fn get_code_hash(&self, address: &Address) -> Bytes32 {
unsafe {
assert!((*self.host).get_code_size.is_some());
(*self.host).get_code_hash.unwrap()(self.context, address as *const Address)
}
}
pub fn copy_code(&self, address: &Address, code_offset: usize, buffer: &mut [u8]) -> usize {
unsafe {
assert!((*self.host).copy_code.is_some());
(*self.host).copy_code.unwrap()(
self.context,
address as *const Address,
code_offset,
buffer.as_mut_ptr(),
buffer.len(),
)
}
}
pub fn selfdestruct(&mut self, address: &Address, beneficiary: &Address) {
unsafe {
assert!((*self.host).selfdestruct.is_some());
(*self.host).selfdestruct.unwrap()(
self.context,
address as *const Address,
beneficiary as *const Address,
)
}
}
pub fn call(&mut self, message: &ExecutionMessage) -> ExecutionResult {
let input = message.input();
let input_size = if let Some(input) = input {
input.len()
} else {
0
};
let input_data = if let Some(input) = input {
input.as_ptr()
} else {
std::ptr::null() as *const u8
};
let message = ffi::evmc_message {
kind: message.kind(),
flags: message.flags(),
depth: message.depth(),
gas: message.gas(),
destination: *message.destination(),
sender: *message.sender(),
input_data: input_data,
input_size: input_size,
value: *message.value(),
create2_salt: *message.create2_salt(),
};
unsafe {
assert!((*self.host).call.is_some());
(*self.host).call.unwrap()(self.context, &message as *const ffi::evmc_message).into()
}
}
pub fn get_block_hash(&self, num: i64) -> Bytes32 {
unsafe {
assert!((*self.host).get_block_hash.is_some());
(*self.host).get_block_hash.unwrap()(self.context, num)
}
}
pub fn emit_log(&mut self, address: &Address, data: &[u8], topics: &[Bytes32]) {
unsafe {
assert!((*self.host).emit_log.is_some());
(*self.host).emit_log.unwrap()(
self.context,
address as *const Address,
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 {
status_code: result.status_code,
gas_left: result.gas_left,
output: if result.output_data.is_null() {
assert!(result.output_size == 0);
None
} else if result.output_size == 0 {
None
} else {
Some(from_buf_raw::<u8>(result.output_data, result.output_size))
},
create_address: Some(result.create_address),
};
if result.release.is_some() {
unsafe {
result.release.unwrap()(&result as *const ffi::evmc_result);
}
}
ret
}
}
fn allocate_output_data(output: Option<&Vec<u8>>) -> (*const u8, usize) {
if let Some(buf) = output {
let buf_len = buf.len();
let memlayout = std::alloc::Layout::from_size_align(buf_len, 1).expect("Bad layout");
let new_buf = unsafe { std::alloc::alloc(memlayout) };
unsafe {
std::ptr::copy(buf.as_ptr(), new_buf, buf_len);
}
(new_buf as *const u8, buf_len)
} else {
(std::ptr::null(), 0)
}
}
unsafe fn deallocate_output_data(ptr: *const u8, size: usize) {
if !ptr.is_null() {
let buf_layout = std::alloc::Layout::from_size_align(size, 1).expect("Bad layout");
std::alloc::dealloc(ptr as *mut u8, buf_layout);
}
}
impl Into<*const ffi::evmc_result> for ExecutionResult {
fn into(self) -> *const ffi::evmc_result {
let mut result: ffi::evmc_result = self.into();
result.release = Some(release_heap_result);
Box::into_raw(Box::new(result))
}
}
extern "C" fn release_heap_result(result: *const ffi::evmc_result) {
unsafe {
let tmp = Box::from_raw(result as *mut ffi::evmc_result);
deallocate_output_data(tmp.output_data, tmp.output_size);
}
}
impl Into<ffi::evmc_result> for ExecutionResult {
fn into(self) -> ffi::evmc_result {
let (buffer, len) = allocate_output_data(self.output.as_ref());
ffi::evmc_result {
status_code: self.status_code,
gas_left: self.gas_left,
output_data: buffer,
output_size: len,
release: Some(release_stack_result),
create_address: if self.create_address.is_some() {
self.create_address.unwrap()
} else {
Address { bytes: [0u8; 20] }
},
padding: [0u8; 4],
}
}
}
extern "C" fn release_stack_result(result: *const ffi::evmc_result) {
unsafe {
let tmp = *result;
deallocate_output_data(tmp.output_data, tmp.output_size);
}
}
impl From<&ffi::evmc_message> for ExecutionMessage {
fn from(message: &ffi::evmc_message) -> Self {
ExecutionMessage {
kind: message.kind,
flags: message.flags,
depth: message.depth,
gas: message.gas,
destination: message.destination,
sender: message.sender,
input: if message.input_data.is_null() {
assert!(message.input_size == 0);
None
} else if message.input_size == 0 {
None
} else {
Some(from_buf_raw::<u8>(message.input_data, message.input_size))
},
value: message.value,
create2_salt: message.create2_salt,
}
}
}
fn from_buf_raw<T>(ptr: *const T, size: usize) -> Vec<T> {
let mut buf = Vec::with_capacity(size);
unsafe {
buf.set_len(size);
std::ptr::copy(ptr, buf.as_mut_ptr(), size);
}
buf
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn result_new() {
let r = ExecutionResult::new(StatusCode::EVMC_FAILURE, 420, None);
assert!(r.status_code() == StatusCode::EVMC_FAILURE);
assert!(r.gas_left() == 420);
assert!(r.output().is_none());
assert!(r.create_address().is_none());
}
extern "C" fn test_result_dispose(result: *const ffi::evmc_result) {
unsafe {
if !result.is_null() {
let owned = *result;
Vec::from_raw_parts(
owned.output_data as *mut u8,
owned.output_size,
owned.output_size,
);
}
}
}
#[test]
fn result_from_ffi() {
let f = ffi::evmc_result {
status_code: StatusCode::EVMC_SUCCESS,
gas_left: 1337,
output_data: Box::into_raw(Box::new([0xde, 0xad, 0xbe, 0xef])) as *const u8,
output_size: 4,
release: Some(test_result_dispose),
create_address: Address { bytes: [0u8; 20] },
padding: [0u8; 4],
};
let r: ExecutionResult = f.into();
assert!(r.status_code() == StatusCode::EVMC_SUCCESS);
assert!(r.gas_left() == 1337);
assert!(r.output().is_some());
assert!(r.output().unwrap().len() == 4);
assert!(r.create_address().is_some());
}
#[test]
fn result_into_heap_ffi() {
let r = ExecutionResult::new(
StatusCode::EVMC_FAILURE,
420,
Some(&[0xc0, 0xff, 0xee, 0x71, 0x75]),
);
let f: *const ffi::evmc_result = r.into();
assert!(!f.is_null());
unsafe {
assert!((*f).status_code == StatusCode::EVMC_FAILURE);
assert!((*f).gas_left == 420);
assert!(!(*f).output_data.is_null());
assert!((*f).output_size == 5);
assert!(
std::slice::from_raw_parts((*f).output_data, 5) as &[u8]
== &[0xc0, 0xff, 0xee, 0x71, 0x75]
);
assert!((*f).create_address.bytes == [0u8; 20]);
if (*f).release.is_some() {
(*f).release.unwrap()(f);
}
}
}
#[test]
fn result_into_heap_ffi_empty_data() {
let r = ExecutionResult::new(StatusCode::EVMC_FAILURE, 420, None);
let f: *const ffi::evmc_result = r.into();
assert!(!f.is_null());
unsafe {
assert!((*f).status_code == StatusCode::EVMC_FAILURE);
assert!((*f).gas_left == 420);
assert!((*f).output_data.is_null());
assert!((*f).output_size == 0);
assert!((*f).create_address.bytes == [0u8; 20]);
if (*f).release.is_some() {
(*f).release.unwrap()(f);
}
}
}
#[test]
fn result_into_stack_ffi() {
let r = ExecutionResult::new(
StatusCode::EVMC_FAILURE,
420,
Some(&[0xc0, 0xff, 0xee, 0x71, 0x75]),
);
let f: ffi::evmc_result = r.into();
unsafe {
assert!(f.status_code == StatusCode::EVMC_FAILURE);
assert!(f.gas_left == 420);
assert!(!f.output_data.is_null());
assert!(f.output_size == 5);
assert!(
std::slice::from_raw_parts(f.output_data, 5) as &[u8]
== &[0xc0, 0xff, 0xee, 0x71, 0x75]
);
assert!(f.create_address.bytes == [0u8; 20]);
if f.release.is_some() {
f.release.unwrap()(&f);
}
}
}
#[test]
fn result_into_stack_ffi_empty_data() {
let r = ExecutionResult::new(StatusCode::EVMC_FAILURE, 420, None);
let f: ffi::evmc_result = r.into();
unsafe {
assert!(f.status_code == StatusCode::EVMC_FAILURE);
assert!(f.gas_left == 420);
assert!(f.output_data.is_null());
assert!(f.output_size == 0);
assert!(f.create_address.bytes == [0u8; 20]);
if f.release.is_some() {
f.release.unwrap()(&f);
}
}
}
#[test]
fn message_new_with_input() {
let input = vec![0xc0, 0xff, 0xee];
let destination = Address { bytes: [32u8; 20] };
let sender = Address { bytes: [128u8; 20] };
let value = Uint256 { bytes: [0u8; 32] };
let create2_salt = Bytes32 { bytes: [255u8; 32] };
let ret = ExecutionMessage::new(
MessageKind::EVMC_CALL,
44,
66,
4466,
destination,
sender,
Some(&input),
value,
create2_salt,
);
assert_eq!(ret.kind(), MessageKind::EVMC_CALL);
assert_eq!(ret.flags(), 44);
assert_eq!(ret.depth(), 66);
assert_eq!(ret.gas(), 4466);
assert_eq!(*ret.destination(), destination);
assert_eq!(*ret.sender(), sender);
assert!(ret.input().is_some());
assert_eq!(*ret.input().unwrap(), input);
assert_eq!(*ret.value(), value);
assert_eq!(*ret.create2_salt(), create2_salt);
}
#[test]
fn message_from_ffi() {
let destination = Address { bytes: [32u8; 20] };
let sender = Address { bytes: [128u8; 20] };
let value = Uint256 { bytes: [0u8; 32] };
let create2_salt = Bytes32 { bytes: [255u8; 32] };
let msg = ffi::evmc_message {
kind: MessageKind::EVMC_CALL,
flags: 44,
depth: 66,
gas: 4466,
destination: destination,
sender: sender,
input_data: std::ptr::null(),
input_size: 0,
value: value,
create2_salt: create2_salt,
};
let ret: ExecutionMessage = (&msg).into();
assert_eq!(ret.kind(), msg.kind);
assert_eq!(ret.flags(), msg.flags);
assert_eq!(ret.depth(), msg.depth);
assert_eq!(ret.gas(), msg.gas);
assert_eq!(*ret.destination(), msg.destination);
assert_eq!(*ret.sender(), msg.sender);
assert!(ret.input().is_none());
assert_eq!(*ret.value(), msg.value);
assert_eq!(*ret.create2_salt(), msg.create2_salt);
}
#[test]
fn message_from_ffi_with_input() {
let input = vec![0xc0, 0xff, 0xee];
let destination = Address { bytes: [32u8; 20] };
let sender = Address { bytes: [128u8; 20] };
let value = Uint256 { bytes: [0u8; 32] };
let create2_salt = Bytes32 { bytes: [255u8; 32] };
let msg = ffi::evmc_message {
kind: MessageKind::EVMC_CALL,
flags: 44,
depth: 66,
gas: 4466,
destination: destination,
sender: sender,
input_data: input.as_ptr(),
input_size: input.len(),
value: value,
create2_salt: create2_salt,
};
let ret: ExecutionMessage = (&msg).into();
assert_eq!(ret.kind(), msg.kind);
assert_eq!(ret.flags(), msg.flags);
assert_eq!(ret.depth(), msg.depth);
assert_eq!(ret.gas(), msg.gas);
assert_eq!(*ret.destination(), msg.destination);
assert_eq!(*ret.sender(), msg.sender);
assert!(ret.input().is_some());
assert_eq!(*ret.input().unwrap(), input);
assert_eq!(*ret.value(), msg.value);
assert_eq!(*ret.create2_salt(), msg.create2_salt);
}
unsafe extern "C" fn get_dummy_tx_context(
_context: *mut ffi::evmc_host_context,
) -> ffi::evmc_tx_context {
ffi::evmc_tx_context {
tx_gas_price: Uint256 { bytes: [0u8; 32] },
tx_origin: Address { bytes: [0u8; 20] },
block_coinbase: Address { bytes: [0u8; 20] },
block_number: 42,
block_timestamp: 235117,
block_gas_limit: 105023,
block_difficulty: Uint256 { bytes: [0xaa; 32] },
chain_id: Uint256::default(),
}
}
unsafe extern "C" fn get_dummy_code_size(
_context: *mut ffi::evmc_host_context,
_addr: *const Address,
) -> usize {
105023 as usize
}
unsafe extern "C" fn execute_call(
_context: *mut ffi::evmc_host_context,
_msg: *const ffi::evmc_message,
) -> ffi::evmc_result {
let msg = *_msg;
let success = if msg.input_size != 0 && msg.input_data == std::ptr::null() {
false
} else if msg.input_size == 0 && msg.input_data != std::ptr::null() {
false
} else {
true
};
ffi::evmc_result {
status_code: if success {
StatusCode::EVMC_SUCCESS
} else {
StatusCode::EVMC_INTERNAL_ERROR
},
gas_left: 2,
output_data: msg.input_data,
output_size: msg.input_size,
release: None,
create_address: Address::default(),
padding: [0u8; 4],
}
}
fn get_dummy_host_interface() -> ffi::evmc_host_interface {
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: Some(execute_call),
get_tx_context: Some(get_dummy_tx_context),
get_block_hash: None,
emit_log: None,
}
}
#[test]
fn execution_context() {
let host_context = std::ptr::null_mut();
let host_interface = get_dummy_host_interface();
let exe_context = ExecutionContext::new(&host_interface, host_context);
let a = exe_context.get_tx_context();
let b = unsafe { get_dummy_tx_context(host_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);
}
#[test]
fn get_code_size() {
let test_addr = Address { bytes: [0u8; 20] };
let host = get_dummy_host_interface();
let host_context = std::ptr::null_mut();
let mut exe_context = ExecutionContext::new(&host, host_context);
let a: usize = 105023;
let b = exe_context.get_code_size(&test_addr);
assert_eq!(a, b);
}
#[test]
fn test_call_empty_data() {
let test_addr = Address::default();
let host = get_dummy_host_interface();
let host_context = std::ptr::null_mut();
let mut exe_context = ExecutionContext::new(&host, host_context);
let message = ExecutionMessage::new(
MessageKind::EVMC_CALL,
0,
0,
6566,
test_addr,
test_addr,
None,
Uint256::default(),
Bytes32::default(),
);
let b = exe_context.call(&message);
assert_eq!(b.status_code(), StatusCode::EVMC_SUCCESS);
assert_eq!(b.gas_left(), 2);
assert!(b.output().is_none());
assert!(b.create_address().is_some());
assert_eq!(b.create_address().unwrap(), &Address::default());
}
#[test]
fn test_call_with_data() {
let test_addr = Address::default();
let host = get_dummy_host_interface();
let host_context = std::ptr::null_mut();
let mut exe_context = ExecutionContext::new(&host, host_context);
let data = vec![0xc0, 0xff, 0xfe];
let message = ExecutionMessage::new(
MessageKind::EVMC_CALL,
0,
0,
6566,
test_addr,
test_addr,
Some(&data),
Uint256::default(),
Bytes32::default(),
);
let b = exe_context.call(&message);
assert_eq!(b.status_code(), StatusCode::EVMC_SUCCESS);
assert_eq!(b.gas_left(), 2);
assert!(b.output().is_some());
assert_eq!(b.output().unwrap(), &data);
assert!(b.create_address().is_some());
assert_eq!(b.create_address().unwrap(), &Address::default());
}
}