Skip to content

Commit 5110194

Browse files
authored
feat!: provide more context to Precompile::call (#109)
feat: provide more context to Precompile::call
1 parent 65096c1 commit 5110194

File tree

1 file changed

+97
-40
lines changed

1 file changed

+97
-40
lines changed

crates/evm/src/precompiles.rs

Lines changed: 97 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use alloc::{borrow::Cow, boxed::Box, string::String, sync::Arc};
44
use alloy_consensus::transaction::Either;
55
use alloy_primitives::{
66
map::{HashMap, HashSet},
7-
Address, Bytes,
7+
Address, Bytes, U256,
88
};
99
use revm::{
1010
context::{Cfg, ContextTr, LocalContextTr},
@@ -146,9 +146,12 @@ impl PrecompilesMap {
146146
Cow::Owned(owned) => owned,
147147
};
148148

149-
for (addr, precompile_fn) in static_precompiles.inner() {
150-
let dyn_precompile: DynPrecompile = (*precompile_fn).into();
151-
dynamic.inner.insert(*addr, dyn_precompile);
149+
for (addr, precompile_fn) in
150+
static_precompiles.inner().iter().map(|(addr, f)| (addr, *f))
151+
{
152+
let precompile =
153+
move |input: PrecompileInput<'_>| precompile_fn(input.data, input.gas);
154+
dynamic.inner.insert(*addr, precompile.into());
152155
dynamic.addresses.insert(*addr);
153156
}
154157

@@ -172,7 +175,9 @@ impl PrecompilesMap {
172175
/// Gets a reference to the precompile at the given address.
173176
pub fn get(&self, address: &Address) -> Option<impl Precompile + '_> {
174177
match self {
175-
Self::Builtin(precompiles) => precompiles.get(address).map(Either::Left),
178+
Self::Builtin(precompiles) => precompiles
179+
.get(address)
180+
.map(|f| Either::Left(|input: PrecompileInput<'_>| f(input.data, input.gas))),
176181
Self::Dynamic(dyn_precompiles) => dyn_precompiles.inner.get(address).map(Either::Right),
177182
}
178183
}
@@ -212,11 +217,9 @@ impl<CTX: ContextTr> PrecompileProvider<CTX> for PrecompilesMap {
212217
gas_limit: u64,
213218
) -> Result<Option<InterpreterResult>, String> {
214219
// Get the precompile at the address
215-
let precompile = self.get(address);
216-
217-
if precompile.is_none() {
220+
let Some(precompile) = self.get(address) else {
218221
return Ok(None);
219-
}
222+
};
220223

221224
let mut result = InterpreterResult {
222225
result: InstructionResult::Return,
@@ -239,8 +242,12 @@ impl<CTX: ContextTr> PrecompileProvider<CTX> for PrecompilesMap {
239242
CallInput::Bytes(bytes) => bytes.as_ref(),
240243
};
241244

242-
let precompile_result =
243-
precompile.expect("None case already handled").call(input_bytes, gas_limit);
245+
let precompile_result = precompile.call(PrecompileInput {
246+
data: input_bytes,
247+
gas: gas_limit,
248+
caller: inputs.caller_address,
249+
value: inputs.call_value,
250+
});
244251

245252
match precompile_result {
246253
Ok(output) => {
@@ -281,15 +288,6 @@ impl core::fmt::Debug for DynPrecompile {
281288
}
282289
}
283290

284-
impl<'a, F> From<F> for DynPrecompile
285-
where
286-
F: FnOnce(&'a [u8], u64) -> PrecompileResult + Precompile + Send + Sync + 'static,
287-
{
288-
fn from(f: F) -> Self {
289-
Self(Arc::new(f))
290-
}
291-
}
292-
293291
/// A mutable representation of precompiles that allows for runtime modification.
294292
///
295293
/// This structure stores dynamic precompiles that can be modified at runtime,
@@ -308,38 +306,60 @@ impl core::fmt::Debug for DynPrecompiles {
308306
}
309307
}
310308

309+
/// Input for a precompile call.
310+
#[derive(Debug, Clone)]
311+
pub struct PrecompileInput<'a> {
312+
/// Input data bytes.
313+
pub data: &'a [u8],
314+
/// Gas limit.
315+
pub gas: u64,
316+
/// Caller address.
317+
pub caller: Address,
318+
/// Value sent with the call.
319+
pub value: U256,
320+
}
321+
311322
/// Trait for implementing precompiled contracts.
312323
pub trait Precompile {
313-
/// Execute the precompile with the given input data and gas limit.
314-
fn call(&self, data: &[u8], gas: u64) -> PrecompileResult;
324+
/// Execute the precompile with the given input data, gas limit, and caller address.
325+
fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult;
315326
}
316327

317328
impl<F> Precompile for F
318329
where
319-
F: Fn(&[u8], u64) -> PrecompileResult + Send + Sync,
330+
F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync,
331+
{
332+
fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
333+
self(input)
334+
}
335+
}
336+
337+
impl<F> From<F> for DynPrecompile
338+
where
339+
F: Fn(PrecompileInput<'_>) -> PrecompileResult + Send + Sync + 'static,
320340
{
321-
fn call(&self, data: &[u8], gas: u64) -> PrecompileResult {
322-
self(data, gas)
341+
fn from(f: F) -> Self {
342+
Self(Arc::new(f))
323343
}
324344
}
325345

326346
impl Precompile for DynPrecompile {
327-
fn call(&self, data: &[u8], gas: u64) -> PrecompileResult {
328-
self.0.call(data, gas)
347+
fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
348+
self.0.call(input)
329349
}
330350
}
331351

332352
impl Precompile for &DynPrecompile {
333-
fn call(&self, data: &[u8], gas: u64) -> PrecompileResult {
334-
self.0.call(data, gas)
353+
fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
354+
self.0.call(input)
335355
}
336356
}
337357

338358
impl<A: Precompile, B: Precompile> Precompile for Either<A, B> {
339-
fn call(&self, data: &[u8], gas: u64) -> PrecompileResult {
359+
fn call(&self, input: PrecompileInput<'_>) -> PrecompileResult {
340360
match self {
341-
Self::Left(p) => p.call(data, gas),
342-
Self::Right(p) => p.call(data, gas),
361+
Self::Left(p) => p.call(input),
362+
Self::Right(p) => p.call(input),
343363
}
344364
}
345365
}
@@ -371,7 +391,14 @@ mod tests {
371391
_ => panic!("Expected dynamic precompiles"),
372392
};
373393

374-
let result = dyn_precompile.call(&test_input, gas_limit).unwrap();
394+
let result = dyn_precompile
395+
.call(PrecompileInput {
396+
data: &test_input,
397+
gas: gas_limit,
398+
caller: Address::ZERO,
399+
value: U256::ZERO,
400+
})
401+
.unwrap();
375402
assert_eq!(result.bytes, test_input, "Identity precompile should return the input data");
376403

377404
// define a function to modify the precompile
@@ -381,7 +408,7 @@ mod tests {
381408
// define a function to modify the precompile to always return a constant value
382409
spec_precompiles.map_precompile(&identity_address, move |_original_dyn| {
383410
// create a new DynPrecompile that always returns our constant
384-
|_data: &[u8], _gas: u64| -> PrecompileResult {
411+
|_input: PrecompileInput<'_>| -> PrecompileResult {
385412
Ok(PrecompileOutput { gas_used: 10, bytes: Bytes::from_static(b"constant value") })
386413
}
387414
.into()
@@ -395,7 +422,14 @@ mod tests {
395422
_ => panic!("Expected dynamic precompiles"),
396423
};
397424

398-
let result = dyn_precompile.call(&test_input, gas_limit).unwrap();
425+
let result = dyn_precompile
426+
.call(PrecompileInput {
427+
data: &test_input,
428+
gas: gas_limit,
429+
caller: Address::ZERO,
430+
value: U256::ZERO,
431+
})
432+
.unwrap();
399433
assert_eq!(
400434
result.bytes, constant_bytes,
401435
"Modified precompile should return the constant value"
@@ -409,15 +443,22 @@ mod tests {
409443
let gas_limit = 1000;
410444

411445
// define a closure that implements the precompile functionality
412-
let closure_precompile = |data: &[u8], _gas: u64| -> PrecompileResult {
446+
let closure_precompile = |input: PrecompileInput<'_>| -> PrecompileResult {
413447
let mut output = b"processed: ".to_vec();
414-
output.extend_from_slice(data.as_ref());
448+
output.extend_from_slice(input.data.as_ref());
415449
Ok(PrecompileOutput { gas_used: 15, bytes: Bytes::from(output) })
416450
};
417451

418452
let dyn_precompile: DynPrecompile = closure_precompile.into();
419453

420-
let result = dyn_precompile.call(&test_input, gas_limit).unwrap();
454+
let result = dyn_precompile
455+
.call(PrecompileInput {
456+
data: &test_input,
457+
gas: gas_limit,
458+
caller: Address::ZERO,
459+
value: U256::ZERO,
460+
})
461+
.unwrap();
421462
assert_eq!(result.gas_used, 15);
422463
assert_eq!(result.bytes, expected_output);
423464
}
@@ -434,7 +475,15 @@ mod tests {
434475
let precompile = spec_precompiles.get(&identity_address);
435476
assert!(precompile.is_some(), "Identity precompile should exist");
436477

437-
let result = precompile.unwrap().call(&test_input, gas_limit).unwrap();
478+
let result = precompile
479+
.unwrap()
480+
.call(PrecompileInput {
481+
data: &test_input,
482+
gas: gas_limit,
483+
caller: Address::ZERO,
484+
value: U256::ZERO,
485+
})
486+
.unwrap();
438487
assert_eq!(result.bytes, test_input, "Identity precompile should return the input data");
439488

440489
let nonexistent_address = address!("0x0000000000000000000000000000000000000099");
@@ -452,7 +501,15 @@ mod tests {
452501
"Identity precompile should exist after conversion to dynamic"
453502
);
454503

455-
let result = dyn_precompile.unwrap().call(&test_input, gas_limit).unwrap();
504+
let result = dyn_precompile
505+
.unwrap()
506+
.call(PrecompileInput {
507+
data: &test_input,
508+
gas: gas_limit,
509+
caller: Address::ZERO,
510+
value: U256::ZERO,
511+
})
512+
.unwrap();
456513
assert_eq!(
457514
result.bytes, test_input,
458515
"Identity precompile should return the input data after conversion to dynamic"

0 commit comments

Comments
 (0)