Sitelet https://github.com/ipsilon/evmc/commit/272276b4059f130378d83ca15a41b08d2749996f
Skip to content
This repository was archived by the owner on Apr 8, 2026. It is now read-only.

Commit 272276b

Browse files
committed
rust: Add helpers for execution result handling
1 parent 9608bab commit 272276b

2 files changed

Lines changed: 192 additions & 1 deletion

File tree

‎.codespell-whitelist‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
iff
2+
mut

‎bindings/rust/evmc-vm/src/lib.rs‎

Lines changed: 191 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,196 @@ pub extern crate evmc_sys;
22
pub use evmc_sys as ffi;
33

44
// TODO: Add helpers for host interface
5-
// TODO: Add convenient helpers for evmc_result
65
// TODO: Add convenient helpers for evmc_execute
76
// TODO: Add a derive macro here for creating evmc_create
7+
8+
/// EVMC result structure.
9+
pub struct ExecutionResult {
10+
status_code: ffi::evmc_status_code,
11+
gas_left: i64,
12+
output: Option<Vec<u8>>,
13+
create_address: ffi::evmc_address,
14+
}
15+
16+
impl ExecutionResult {
17+
pub fn new(
18+
_status_code: ffi::evmc_status_code,
19+
_gas_left: i64,
20+
_output: Option<Vec<u8>>,
21+
_create_address: ffi::evmc_address,
22+
) -> Self {
23+
ExecutionResult {
24+
status_code: _status_code,
25+
gas_left: _gas_left,
26+
output: _output,
27+
create_address: _create_address,
28+
}
29+
}
30+
31+
pub fn get_status_code(&self) -> ffi::evmc_status_code {
32+
self.status_code
33+
}
34+
35+
pub fn get_gas_left(&self) -> i64 {
36+
self.gas_left
37+
}
38+
39+
pub fn get_output(&self) -> Option<&Vec<u8>> {
40+
self.output.as_ref()
41+
}
42+
43+
pub fn get_create_address(&self) -> Option<&ffi::evmc_address> {
44+
// Only return Some if the address is valid (e.g. the status is EVMC_SUCCESS)
45+
if self.status_code == ffi::evmc_status_code::EVMC_SUCCESS {
46+
Some(&self.create_address)
47+
} else {
48+
None
49+
}
50+
}
51+
}
52+
53+
impl From<ffi::evmc_result> for ExecutionResult {
54+
fn from(result: ffi::evmc_result) -> Self {
55+
let ret = ExecutionResult {
56+
status_code: result.status_code,
57+
gas_left: result.gas_left,
58+
output: if !result.output_data.is_null() {
59+
// Pre-allocate a vector.
60+
let mut buf: Vec<u8> = Vec::with_capacity(result.output_size);
61+
62+
unsafe {
63+
// Set the len of the vec manually.
64+
buf.set_len(result.output_size);
65+
// Copy from the C struct's buffer to the vec's buffer.
66+
std::ptr::copy(result.output_data, buf.as_mut_ptr(), result.output_size);
67+
}
68+
69+
Some(buf)
70+
} else {
71+
None
72+
},
73+
create_address: result.create_address,
74+
};
75+
76+
// Release allocated ffi struct.
77+
if result.release.is_some() {
78+
unsafe {
79+
result.release.unwrap()(&result as *const ffi::evmc_result);
80+
}
81+
}
82+
83+
ret
84+
}
85+
}
86+
87+
/// Returns a pointer to a heap-allocated evmc_result.
88+
impl Into<*const ffi::evmc_result> for ExecutionResult {
89+
fn into(self) -> *const ffi::evmc_result {
90+
let (buffer, len) = if let Some(buf) = self.output {
91+
let buf_len = buf.len();
92+
93+
// Manually allocate heap memory for the new home of the output buffer.
94+
let memlayout = std::alloc::Layout::from_size_align(buf_len, 1).expect("Bad layout");
95+
let new_buf = unsafe { std::alloc::alloc(memlayout) };
96+
unsafe {
97+
// Copy the data into the allocated buffer.
98+
std::ptr::copy(buf.as_ptr(), new_buf, buf_len);
99+
}
100+
101+
(new_buf as *const u8, buf_len)
102+
} else {
103+
(std::ptr::null(), 0)
104+
};
105+
106+
Box::into_raw(Box::new(ffi::evmc_result {
107+
status_code: self.status_code,
108+
gas_left: self.gas_left,
109+
output_data: buffer,
110+
output_size: len,
111+
release: Some(release_result),
112+
create_address: self.create_address,
113+
padding: [0u8; 4],
114+
}))
115+
}
116+
}
117+
118+
/// Callback to pass across FFI, de-allocating the struct.
119+
extern "C" fn release_result(result: *const ffi::evmc_result) {
120+
unsafe {
121+
let tmp = Box::from_raw(result as *mut ffi::evmc_result);
122+
if tmp.output_data.is_null() {
123+
let buf_layout =
124+
std::alloc::Layout::from_size_align(tmp.output_size, 1).expect("Bad layout");
125+
std::alloc::dealloc(tmp.output_data as *mut u8, buf_layout);
126+
}
127+
Box::from_raw(result as *mut ffi::evmc_result);
128+
}
129+
}
130+
131+
#[cfg(test)]
132+
mod tests {
133+
use super::*;
134+
use evmc_sys as ffi;
135+
136+
#[test]
137+
fn new_result() {
138+
let r = ExecutionResult::new(
139+
ffi::evmc_status_code::EVMC_FAILURE,
140+
420,
141+
None,
142+
ffi::evmc_address { bytes: [0u8; 20] },
143+
);
144+
145+
assert!(r.get_status_code() == ffi::evmc_status_code::EVMC_FAILURE);
146+
assert!(r.get_gas_left() == 420);
147+
assert!(r.get_output().is_none());
148+
149+
// Ensure that an address is not returned if it is not valid, per status code.
150+
assert!(r.get_create_address().is_none());
151+
}
152+
153+
#[test]
154+
fn from_ffi() {
155+
let f = ffi::evmc_result {
156+
status_code: ffi::evmc_status_code::EVMC_SUCCESS,
157+
gas_left: 1337,
158+
output_data: Box::into_raw(Box::new([0xde, 0xad, 0xbe, 0xef])) as *const u8,
159+
output_size: 4,
160+
release: None,
161+
create_address: ffi::evmc_address { bytes: [0u8; 20] },
162+
padding: [0u8; 4],
163+
};
164+
165+
let r: ExecutionResult = f.into();
166+
167+
assert!(r.get_status_code() == ffi::evmc_status_code::EVMC_SUCCESS);
168+
assert!(r.get_gas_left() == 1337);
169+
assert!(r.get_output().is_some());
170+
assert!(r.get_output().unwrap().len() == 4);
171+
assert!(r.get_create_address().is_some());
172+
}
173+
174+
#[test]
175+
fn into_ffi() {
176+
let r = ExecutionResult::new(
177+
ffi::evmc_status_code::EVMC_FAILURE,
178+
420,
179+
Some(vec![0xc0, 0xff, 0xee, 0x71, 0x75]),
180+
ffi::evmc_address { bytes: [0u8; 20] },
181+
);
182+
183+
let f: *const ffi::evmc_result = r.into();
184+
assert!(!f.is_null());
185+
unsafe {
186+
assert!((*f).status_code == ffi::evmc_status_code::EVMC_FAILURE);
187+
assert!((*f).gas_left == 420);
188+
assert!(!(*f).output_data.is_null());
189+
assert!((*f).output_size == 5);
190+
assert!(
191+
std::slice::from_raw_parts((*f).output_data, 5) as &[u8]
192+
== &[0xc0, 0xff, 0xee, 0x71, 0x75]
193+
);
194+
assert!((*f).create_address.bytes == [0u8; 20]);
195+
}
196+
}
197+
}

0 commit comments

Comments
 (0)