1
+ use crate :: helpers:: Ctx ;
1
2
use revm:: {
3
+ context_interface:: context:: ContextError ,
2
4
interpreter:: {
3
- CallInputs , CallOutcome , CreateInputs , CreateOutcome , EOFCreateInputs , Gas ,
4
- InstructionResult , Interpreter , InterpreterResult , InterpreterTypes ,
5
+ CallInputs , CallOutcome , CreateInputs , CreateOutcome , EOFCreateInputs , Interpreter ,
6
+ InterpreterTypes ,
5
7
} ,
6
- primitives:: Bytes ,
7
- Inspector ,
8
+ Database , Inspector ,
8
9
} ;
9
10
use std:: time:: { Duration , Instant } ;
10
11
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
-
29
12
/// A revm [`Inspector`] that limits wallclock time spent on execution.
30
13
///
31
14
/// This inspector will stop execution at the beginning and end of each
@@ -41,14 +24,8 @@ const CREATE_TIMEOUT: CreateOutcome = CreateOutcome {
41
24
/// - any invalid opcode
42
25
///
43
26
/// 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.
52
29
///
53
30
/// ## Usage Note
54
31
///
@@ -63,6 +40,7 @@ const CREATE_TIMEOUT: CreateOutcome = CreateOutcome {
63
40
/// discarded.
64
41
///
65
42
/// [EIP-150]: https://eips.ethereum.org/EIPS/eip-150
43
+ /// [`EVMError::Custom`]: revm::context::result::EVMError::Custom
66
44
#[ derive( Debug , Clone , Copy ) ]
67
45
pub struct TimeLimit {
68
46
duration : Duration ,
@@ -93,63 +71,60 @@ impl TimeLimit {
93
71
}
94
72
}
95
73
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 > ) {
98
84
self . reset ( ) ;
99
85
}
100
86
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) ;
105
89
106
90
None
107
91
}
108
92
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) ;
113
95
}
114
96
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
+
119
100
None
120
101
}
121
102
122
103
fn create_end (
123
104
& mut self ,
124
- _context : & mut Ctx ,
105
+ ctx : & mut Ctx < Db > ,
125
106
_inputs : & CreateInputs ,
126
- outcome : & mut CreateOutcome ,
107
+ _outcome : & mut CreateOutcome ,
127
108
) {
128
- if self . has_elapsed ( ) {
129
- * outcome = CREATE_TIMEOUT ;
130
- }
109
+ check_timeout ! ( self , ctx) ;
131
110
}
132
111
133
112
fn eofcreate (
134
113
& mut self ,
135
- _context : & mut Ctx ,
114
+ ctx : & mut Ctx < Db > ,
136
115
_inputs : & mut EOFCreateInputs ,
137
116
) -> Option < CreateOutcome > {
138
- if self . has_elapsed ( ) {
139
- return Some ( CREATE_TIMEOUT ) ;
140
- }
117
+ check_timeout ! ( self , ctx) ;
141
118
142
119
None
143
120
}
144
121
145
122
fn eofcreate_end (
146
123
& mut self ,
147
- _context : & mut Ctx ,
124
+ ctx : & mut Ctx < Db > ,
148
125
_inputs : & EOFCreateInputs ,
149
- outcome : & mut CreateOutcome ,
126
+ _outcome : & mut CreateOutcome ,
150
127
) {
151
- if self . has_elapsed ( ) {
152
- * outcome = CREATE_TIMEOUT ;
153
- }
128
+ check_timeout ! ( self , ctx) ;
154
129
}
155
130
}
0 commit comments