Skip to content

Commit 98322d2

Browse files
committed
feat: implement jumpdest limiter
1 parent 9d9899d commit 98322d2

File tree

4 files changed

+76
-13
lines changed

4 files changed

+76
-13
lines changed

Cargo.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/evm/src/jumpdest_limiter.rs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
use reth::revm::{
2+
Inspector,
3+
interpreter::{
4+
interpreter::EthInterpreter, Gas, Interpreter, InterpreterAction, InstructionResult,
5+
},
6+
};
7+
8+
/// Inspector that aborts a transaction once it executes more than `limit` `JUMPDEST` opcodes.
9+
///
10+
/// Usage sketch (leave commented out until you want to enable it):
11+
/// - In `TaikoEvmFactory::create_evm_with_inspector`, swap the `NoOpInspector` for
12+
/// `JumpdestLimiter::new(100)` and set the wrapper's `inspect` flag to `true` so every tx is
13+
/// executed with this inspector.
14+
/// - In payload building (`crates/payload/src/builder.rs`) or block execution, match on the EVM
15+
/// error and `continue` to drop the offending tx instead of failing the whole payload:
16+
/// ```
17+
/// // if let Err(BlockExecutionError::Evm { error, .. }) = builder.execute_transaction(tx.clone()) {
18+
/// // if error.is_fatal_external_error() {
19+
/// // // JUMPDEST limit hit; skip this tx
20+
/// // continue;
21+
/// // }
22+
/// // }
23+
/// ```
24+
#[derive(Debug, Clone)]
25+
pub struct JumpdestLimiter {
26+
limit: u64,
27+
count: u64,
28+
}
29+
30+
impl JumpdestLimiter {
31+
pub const fn new(limit: u64) -> Self {
32+
Self { limit, count: 0 }
33+
}
34+
}
35+
36+
impl<CTX> Inspector<CTX, EthInterpreter> for JumpdestLimiter {
37+
fn initialize_interp(&mut self, _interp: &mut Interpreter<EthInterpreter>, _ctx: &mut CTX) {
38+
// Reset per top-level transaction.
39+
self.count = 0;
40+
}
41+
42+
fn step(&mut self, interp: &mut Interpreter<EthInterpreter>, _ctx: &mut CTX) {
43+
const JUMPDEST: u8 = 0x5b;
44+
if interp.bytecode.opcode() == JUMPDEST {
45+
self.count += 1;
46+
if self.count > self.limit {
47+
// Halt execution immediately; upstream sees a fatal external error for this tx.
48+
interp.bytecode.set_action(InterpreterAction::new_halt(
49+
InstructionResult::FatalExternalError,
50+
Gas::new(0),
51+
));
52+
}
53+
}
54+
}
55+
}

crates/evm/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ pub mod evm;
44
pub mod execution;
55
pub mod factory;
66
pub mod handler;
7+
pub mod jumpdest_limiter;
78
pub mod spec;

crates/payload/src/builder.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,14 @@ where
174174
continue;
175175
}
176176
// this is an error that we should treat as fatal for this attempt
177-
Err(err) => return Err(PayloadBuilderError::evm(err)),
177+
Err(err) => {
178+
// To drop transactions halted by `JumpdestLimiter` instead of failing the payload,
179+
// you can match the fatal external error and `continue` here:
180+
// if matches!(err, BlockExecutionError::Evm { .. }) {
181+
// continue;
182+
// }
183+
return Err(PayloadBuilderError::evm(err));
184+
}
178185
};
179186

180187
// update add to total fees

0 commit comments

Comments
 (0)