1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
//! Connection to external EVM tracer.
use crate::go;
use core::fmt::{Display, Formatter, Result as FmtResult};
use std::ffi::{CStr, CString};
/// Creates the trace
pub fn trace(config: &str) -> Result<String, Error> {
// Create a string we can pass into Go
let c_config = CString::new(config).expect("invalid config");
// Generate the trace externally
let result = unsafe { go::CreateTrace(c_config.as_ptr()) };
// Convert the returned string to something we can use in Rust again.
// Also make sure the returned data is copied to rust managed memory.
let c_result = unsafe { CStr::from_ptr(result) };
let result = c_result
.to_str()
.expect("Error translating EVM trace from library")
.to_string();
// We can now free the returned string (memory managed by Go)
unsafe { go::FreeString(c_result.as_ptr()) };
// Return the trace
match result.is_empty() || result.starts_with("Failed") {
true => Ok(format!(
"[\n{{\n\"gas\": 0,\n\"failed\": true,\n\"invalid\": true,\n\"returnValue\": \"{result}\",\n\"structLogs\": []\n}}\n]"
)),
false => Ok(result),
}
}
/// Error type for any geth-utils related failure.
#[derive(Debug, Clone)]
pub enum Error {
/// Error while tracing.
TracingError(String),
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(f, "{:?}", self)
}
}
#[cfg(test)]
mod test {
use eth_types::GethExecTrace;
use crate::trace;
#[test]
fn valid_tx() {
for config in [
// Minimal call tx with gas_limit = 21000
r#"{
"block_constants": {
"gas_limit": "0x52080"
},
"transactions": [
{
"from": "0x00000000000000000000000000000000000000fe",
"to": "0x00000000000000000000000000000000000000ff",
"gas_limit": "0x5208"
}
]
}"#,
// Minimal creation tx with gas_limit = 53000
r#"{
"block_constants": {
"gas_limit": "0xcf080"
},
"transactions": [
{
"from": "0x00000000000000000000000000000000000000fe",
"gas_limit": "0xcf08"
}
]
}"#,
// Normal call tx with gas_limit = 21000 and gas_price = 2 Gwei
r#"{
"block_constants": {
"gas_limit": "0x52080"
},
"accounts": {
"0x00000000000000000000000000000000000000fe": {
"balance": "0x2632e314a000"
}
},
"transactions": [
{
"from": "0x00000000000000000000000000000000000000fe",
"to": "0x00000000000000000000000000000000000000ff",
"gas_limit": "0x5208",
"gas_price": "0x77359400"
}
]
}"#,
] {
let trace_result = trace(config);
assert!(trace_result.is_ok());
// Run over the traces
let trace: Vec<GethExecTrace> = serde_json::from_str(&trace_result.unwrap()).unwrap();
for trace in trace.iter() {
assert!(!trace.invalid);
}
}
}
#[test]
fn invalid_tx() {
for config in [
// Insufficient gas for intrinsic usage
r#"{
"block_constants": {
"gas_limit": "0xcf080"
},
"transactions": [
{
"from": "0x00000000000000000000000000000000000000fe",
"to": "0x00000000000000000000000000000000000000ff"
}
]
}"#,
// Insufficient balance to buy gas
r#"{
"block_constants": {
"gas_limit": "0x52080"
},
"transactions": [
{
"from": "0x00000000000000000000000000000000000000fe",
"to": "0x00000000000000000000000000000000000000ff",
"gas_limit": "0x5208",
"gas_price": "0x1111"
}
]
}"#,
// Insufficient balance to do the first transfer
r#"{
"block_constants": {
"gas_limit": "0x52080"
},
"transactions": [
{
"from": "0x00000000000000000000000000000000000000fe",
"to": "0x00000000000000000000000000000000000000ff",
"value": "0x100",
"gas_limit": "0x5208"
}
]
}"#,
] {
let trace_result = trace(config);
assert!(trace_result.is_ok());
// Run over the traces
let trace: Vec<GethExecTrace> = serde_json::from_str(&trace_result.unwrap()).unwrap();
for trace in trace.iter() {
assert!(trace.invalid);
}
}
}
}