Skip to content

Commit a74ef27

Browse files
authored
refactor: improve timeout error by not hijacking stack too deep (#108)
* refactor: improve timeout error by not hijacking stack too deep * fix: error message * chore: version bump * fix: docs * fix: test
1 parent 91e5f4f commit a74ef27

File tree

3 files changed

+38
-61
lines changed

3 files changed

+38
-61
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "trevm"
3-
version = "0.23.4"
3+
version = "0.23.5"
44
rust-version = "1.83.0"
55
edition = "2021"
66
authors = ["init4"]

src/inspectors/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ mod test {
1818
use std::time::Duration;
1919

2020
#[test]
21-
fn test() {
21+
fn test_timeout() {
2222
let inspector =
2323
Layered::new(TimeLimit::new(Duration::from_micros(10)), SpanningInspector::at_info())
2424
.wrap_around(TestInspector::default());
@@ -31,7 +31,9 @@ mod test {
3131
.fill_cfg(&NoopCfg)
3232
.fill_block(&NoopBlock);
3333

34-
trevm.apply_eip4788(B256::repeat_byte(0xaa)).unwrap();
34+
let err = trevm.apply_eip4788(B256::repeat_byte(0xaa)).unwrap_err();
35+
assert!(matches!(err, revm::context::result::EVMError::Custom(_)));
36+
assert!(format!("{err}").contains("timeout during evm execution"));
3537

3638
assert!(trevm.inner_mut_unchecked().inspector().outer().outer().has_elapsed());
3739
}

src/inspectors/timeout.rs

Lines changed: 33 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,14 @@
1+
use crate::helpers::Ctx;
12
use revm::{
3+
context_interface::context::ContextError,
24
interpreter::{
3-
CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Gas,
4-
InstructionResult, Interpreter, InterpreterResult, InterpreterTypes,
5+
CallInputs, CallOutcome, CreateInputs, CreateOutcome, EOFCreateInputs, Interpreter,
6+
InterpreterTypes,
57
},
6-
primitives::Bytes,
7-
Inspector,
8+
Database, Inspector,
89
};
910
use std::time::{Duration, Instant};
1011

11-
const CALL_TIMEOUT: CallOutcome = CallOutcome {
12-
result: InterpreterResult {
13-
result: InstructionResult::CallTooDeep,
14-
output: Bytes::new(),
15-
gas: Gas::new_spent(0),
16-
},
17-
memory_offset: 0..0,
18-
};
19-
20-
const CREATE_TIMEOUT: CreateOutcome = CreateOutcome {
21-
result: InterpreterResult {
22-
result: InstructionResult::CallTooDeep,
23-
output: Bytes::new(),
24-
gas: Gas::new_spent(0),
25-
},
26-
address: None,
27-
};
28-
2912
/// A revm [`Inspector`] that limits wallclock time spent on execution.
3013
///
3114
/// This inspector will stop execution at the beginning and end of each
@@ -41,14 +24,8 @@ const CREATE_TIMEOUT: CreateOutcome = CreateOutcome {
4124
/// - any invalid opcode
4225
///
4326
/// When execution is terminated by the timer, it will result in a
44-
/// [`InstructionResult::CallTooDeep`]. This is somewhat unintutive. `revm`
45-
/// uses the [`InstructionResult`] enum to represent possible outcomes of a
46-
/// opcode. It requires that the inspector's outcome is a valid
47-
/// [`InstructionResult`], but does not provide a way to represent a custom
48-
/// outcome. This means that the inspector must overload an existing outcome.
49-
/// `CallTooDeep` is used here because it is effectively unreachable in normal
50-
/// `evm` execution due to [EIP-150] call gas forwarding rules, and therefore
51-
/// overloading it is unlikely to cause issues.
27+
/// [`ContextError::Custom`], which will be propagated as an
28+
/// [`EVMError::Custom`] to the caller of the EVM interpreter.
5229
///
5330
/// ## Usage Note
5431
///
@@ -63,6 +40,7 @@ const CREATE_TIMEOUT: CreateOutcome = CreateOutcome {
6340
/// discarded.
6441
///
6542
/// [EIP-150]: https://eips.ethereum.org/EIPS/eip-150
43+
/// [`EVMError::Custom`]: revm::context::result::EVMError::Custom
6644
#[derive(Debug, Clone, Copy)]
6745
pub struct TimeLimit {
6846
duration: Duration,
@@ -93,63 +71,60 @@ impl TimeLimit {
9371
}
9472
}
9573

96-
impl<Ctx, Int: InterpreterTypes> Inspector<Ctx, Int> for TimeLimit {
97-
fn initialize_interp(&mut self, _interp: &mut Interpreter<Int>, _context: &mut Ctx) {
74+
macro_rules! check_timeout {
75+
($self:ident, $ctx:ident) => {
76+
if $self.has_elapsed() {
77+
$ctx.error = Err(ContextError::Custom("timeout during evm execution".to_string()));
78+
}
79+
};
80+
}
81+
82+
impl<Db: Database, Int: InterpreterTypes> Inspector<Ctx<Db>, Int> for TimeLimit {
83+
fn initialize_interp(&mut self, _interp: &mut Interpreter<Int>, _ctx: &mut Ctx<Db>) {
9884
self.reset();
9985
}
10086

101-
fn call(&mut self, _context: &mut Ctx, _inputs: &mut CallInputs) -> Option<CallOutcome> {
102-
if self.has_elapsed() {
103-
return Some(CALL_TIMEOUT);
104-
}
87+
fn call(&mut self, ctx: &mut Ctx<Db>, _inputs: &mut CallInputs) -> Option<CallOutcome> {
88+
check_timeout!(self, ctx);
10589

10690
None
10791
}
10892

109-
fn call_end(&mut self, _context: &mut Ctx, _inputs: &CallInputs, outcome: &mut CallOutcome) {
110-
if self.has_elapsed() {
111-
*outcome = CALL_TIMEOUT;
112-
}
93+
fn call_end(&mut self, ctx: &mut Ctx<Db>, _inputs: &CallInputs, _outcome: &mut CallOutcome) {
94+
check_timeout!(self, ctx);
11395
}
11496

115-
fn create(&mut self, _context: &mut Ctx, _inputs: &mut CreateInputs) -> Option<CreateOutcome> {
116-
if self.has_elapsed() {
117-
return Some(CREATE_TIMEOUT);
118-
}
97+
fn create(&mut self, ctx: &mut Ctx<Db>, _inputs: &mut CreateInputs) -> Option<CreateOutcome> {
98+
check_timeout!(self, ctx);
99+
119100
None
120101
}
121102

122103
fn create_end(
123104
&mut self,
124-
_context: &mut Ctx,
105+
ctx: &mut Ctx<Db>,
125106
_inputs: &CreateInputs,
126-
outcome: &mut CreateOutcome,
107+
_outcome: &mut CreateOutcome,
127108
) {
128-
if self.has_elapsed() {
129-
*outcome = CREATE_TIMEOUT;
130-
}
109+
check_timeout!(self, ctx);
131110
}
132111

133112
fn eofcreate(
134113
&mut self,
135-
_context: &mut Ctx,
114+
ctx: &mut Ctx<Db>,
136115
_inputs: &mut EOFCreateInputs,
137116
) -> Option<CreateOutcome> {
138-
if self.has_elapsed() {
139-
return Some(CREATE_TIMEOUT);
140-
}
117+
check_timeout!(self, ctx);
141118

142119
None
143120
}
144121

145122
fn eofcreate_end(
146123
&mut self,
147-
_context: &mut Ctx,
124+
ctx: &mut Ctx<Db>,
148125
_inputs: &EOFCreateInputs,
149-
outcome: &mut CreateOutcome,
126+
_outcome: &mut CreateOutcome,
150127
) {
151-
if self.has_elapsed() {
152-
*outcome = CREATE_TIMEOUT;
153-
}
128+
check_timeout!(self, ctx);
154129
}
155130
}

0 commit comments

Comments
 (0)