diff --git a/crates/precompile/benches/bench.rs b/crates/precompile/benches/bench.rs index 7fba133b50..faae103a95 100644 --- a/crates/precompile/benches/bench.rs +++ b/crates/precompile/benches/bench.rs @@ -45,7 +45,7 @@ pub fn benchmark_crypto_precompiles(c: &mut Criterion) { u64::MAX, ) .unwrap() - .0; + .gas_used; println!("gas used by regular pairing call: {:?}", res); @@ -60,7 +60,9 @@ pub fn benchmark_crypto_precompiles(c: &mut Criterion) { ) .unwrap(); - let res = run_add(&ecadd_input, ISTANBUL_ADD_GAS_COST, 150).unwrap().0; + let res = run_add(&ecadd_input, ISTANBUL_ADD_GAS_COST, 150) + .unwrap() + .gas_used; println!("gas used by bn128 add precompile: {:?}", res); // === ECRECOVER === @@ -86,7 +88,9 @@ pub fn benchmark_crypto_precompiles(c: &mut Criterion) { message_and_signature[64..128].copy_from_slice(&data); let message_and_signature = Bytes::from(message_and_signature); - let gas = ec_recover_run(&message_and_signature, u64::MAX).unwrap().0; + let gas = ec_recover_run(&message_and_signature, u64::MAX) + .unwrap() + .gas_used; println!("gas used by ecrecover precompile: {:?}", gas); // === POINT_EVALUATION === @@ -103,8 +107,8 @@ pub fn benchmark_crypto_precompiles(c: &mut Criterion) { let gas = 50000; let env = Env::default(); - let (actual_gas, _actual_output) = run(&kzg_input, gas, &env).unwrap(); - println!("gas used by kzg precompile: {:?}", actual_gas); + let output = run(&kzg_input, gas, &env).unwrap(); + println!("gas used by kzg precompile: {:?}", output.gas_used); group.bench_function(group_name("ecrecover precompile"), |b| { b.iter(|| { diff --git a/crates/precompile/src/blake2.rs b/crates/precompile/src/blake2.rs index 057bba74d4..1b4ab9256a 100644 --- a/crates/precompile/src/blake2.rs +++ b/crates/precompile/src/blake2.rs @@ -1,5 +1,5 @@ use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress}; -use revm_primitives::Bytes; +use revm_primitives::{Bytes, PrecompileOutput}; const F_ROUND: u64 = 1; const INPUT_LENGTH: usize = 213; @@ -14,20 +14,20 @@ pub fn run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let input = &input[..]; if input.len() != INPUT_LENGTH { - return Err(Error::Blake2WrongLength); + return Err(Error::Blake2WrongLength.into()); } let f = match input[212] { 1 => true, 0 => false, - _ => return Err(Error::Blake2WrongFinalIndicatorFlag), + _ => return Err(Error::Blake2WrongFinalIndicatorFlag.into()), }; // rounds 4 bytes let rounds = u32::from_be_bytes(input[..4].try_into().unwrap()) as usize; let gas_used = rounds as u64 * F_ROUND; if gas_used > gas_limit { - return Err(Error::OutOfGas); + return Err(Error::OutOfGas.into()); } let mut h = [0u64; 8]; @@ -51,7 +51,7 @@ pub fn run(input: &Bytes, gas_limit: u64) -> PrecompileResult { out[i..i + 8].copy_from_slice(&h.to_le_bytes()); } - Ok((gas_used, out.into())) + Ok(PrecompileOutput::new(gas_used, out.into())) } pub mod algo { diff --git a/crates/precompile/src/bls12_381.rs b/crates/precompile/src/bls12_381.rs index a020e8b13a..75e774ded4 100644 --- a/crates/precompile/src/bls12_381.rs +++ b/crates/precompile/src/bls12_381.rs @@ -109,20 +109,23 @@ mod test { let Some(gas) = vector.gas else { panic!("gas is missing in {test_name}"); }; - let (actual_gas, actual_output) = - res.unwrap_or_else(|e| panic!("precompile call failed for {test_name}: {e}")); + let outcome = res.unwrap_or_else(|e: revm_primitives::PrecompileErrors| { + panic!("precompile call failed for {test_name}: {e}") + }); assert_eq!( - gas, actual_gas, + gas, outcome.gas_used, "expected gas: {}, actual gas: {} in {test_name}", - gas, actual_gas + gas, outcome.gas_used ); let Some(expected) = vector.expected else { panic!("expected output is missing in {test_name}"); }; let expected_output = Bytes::from_hex(expected).unwrap(); assert_eq!( - expected_output, actual_output, - "expected output: {expected_output}, actual output: {actual_output} in {test_name}"); + expected_output, outcome.bytes, + "expected output: {expected_output}, actual output: {:?} in {test_name}", + outcome.bytes + ); } } } diff --git a/crates/precompile/src/bls12_381/g1_add.rs b/crates/precompile/src/bls12_381/g1_add.rs index 9fed002dfc..da671aab49 100644 --- a/crates/precompile/src/bls12_381/g1_add.rs +++ b/crates/precompile/src/bls12_381/g1_add.rs @@ -3,7 +3,7 @@ use crate::{u64_to_address, PrecompileWithAddress}; use blst::{ blst_p1, blst_p1_add_or_double_affine, blst_p1_affine, blst_p1_from_affine, blst_p1_to_affine, }; -use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileResult}; +use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G1ADD precompile. pub const PRECOMPILE: PrecompileWithAddress = @@ -23,14 +23,15 @@ const INPUT_LENGTH: usize = 256; /// See also: pub(super) fn g1_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { if BASE_GAS_FEE > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } if input.len() != INPUT_LENGTH { return Err(PrecompileError::Other(format!( "G1ADD input should be {INPUT_LENGTH} bytes, was {}", input.len() - ))); + )) + .into()); } // NB: There is no subgroup check for the G1 addition precompile. @@ -52,5 +53,5 @@ pub(super) fn g1_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { unsafe { blst_p1_to_affine(&mut p_aff, &p) }; let out = encode_g1_point(&p_aff); - Ok((BASE_GAS_FEE, out)) + Ok(PrecompileOutput::new(BASE_GAS_FEE, out)) } diff --git a/crates/precompile/src/bls12_381/g1_msm.rs b/crates/precompile/src/bls12_381/g1_msm.rs index c0e03b3b18..b62077c6c6 100644 --- a/crates/precompile/src/bls12_381/g1_msm.rs +++ b/crates/precompile/src/bls12_381/g1_msm.rs @@ -6,7 +6,7 @@ use super::{ }; use crate::{u64_to_address, PrecompileWithAddress}; use blst::{blst_p1, blst_p1_affine, blst_p1_from_affine, blst_p1_to_affine, p1_affines}; -use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileResult}; +use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G1MSM precompile. pub const PRECOMPILE: PrecompileWithAddress = @@ -30,13 +30,14 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { "G1MSM input length should be multiple of {}, was {}", g1_mul::INPUT_LENGTH, input_len - ))); + )) + .into()); } let k = input_len / g1_mul::INPUT_LENGTH; let required_gas = msm_required_gas(k, g1_mul::BASE_GAS_FEE); if required_gas > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } let mut g1_points: Vec = Vec::with_capacity(k); @@ -72,7 +73,7 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { // return infinity point if all points are infinity if g1_points.is_empty() { - return Ok((required_gas, [0; 128].into())); + return Ok(PrecompileOutput::new(required_gas, [0; 128].into())); } let points = p1_affines::from(&g1_points); @@ -83,5 +84,5 @@ pub(super) fn g1_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { unsafe { blst_p1_to_affine(&mut multiexp_aff, &multiexp) }; let out = encode_g1_point(&multiexp_aff); - Ok((required_gas, out)) + Ok(PrecompileOutput::new(required_gas, out)) } diff --git a/crates/precompile/src/bls12_381/g1_mul.rs b/crates/precompile/src/bls12_381/g1_mul.rs index 2b62a39c1d..aee9524448 100644 --- a/crates/precompile/src/bls12_381/g1_mul.rs +++ b/crates/precompile/src/bls12_381/g1_mul.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{u64_to_address, PrecompileWithAddress}; use blst::{blst_p1, blst_p1_affine, blst_p1_from_affine, blst_p1_mult, blst_p1_to_affine}; -use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileResult}; +use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G1MUL precompile. pub const PRECOMPILE: PrecompileWithAddress = @@ -25,13 +25,14 @@ pub(super) const INPUT_LENGTH: usize = 160; /// See also: pub(super) fn g1_mul(input: &Bytes, gas_limit: u64) -> PrecompileResult { if BASE_GAS_FEE > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } if input.len() != INPUT_LENGTH { return Err(PrecompileError::Other(format!( "G1MUL input should be {INPUT_LENGTH} bytes, was {}", input.len() - ))); + )) + .into()); } // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. @@ -55,5 +56,5 @@ pub(super) fn g1_mul(input: &Bytes, gas_limit: u64) -> PrecompileResult { unsafe { blst_p1_to_affine(&mut p_aff, &p) }; let out = encode_g1_point(&p_aff); - Ok((BASE_GAS_FEE, out)) + Ok(PrecompileOutput::new(BASE_GAS_FEE, out)) } diff --git a/crates/precompile/src/bls12_381/g2_add.rs b/crates/precompile/src/bls12_381/g2_add.rs index a8f5b85a8e..9b1e26f184 100644 --- a/crates/precompile/src/bls12_381/g2_add.rs +++ b/crates/precompile/src/bls12_381/g2_add.rs @@ -3,7 +3,7 @@ use crate::{u64_to_address, PrecompileWithAddress}; use blst::{ blst_p2, blst_p2_add_or_double_affine, blst_p2_affine, blst_p2_from_affine, blst_p2_to_affine, }; -use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileResult}; +use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2ADD precompile. pub const PRECOMPILE: PrecompileWithAddress = @@ -24,14 +24,15 @@ const INPUT_LENGTH: usize = 512; /// See also pub(super) fn g2_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { if BASE_GAS_FEE > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } if input.len() != INPUT_LENGTH { return Err(PrecompileError::Other(format!( "G2ADD input should be {INPUT_LENGTH} bytes, was {}", input.len() - ))); + )) + .into()); } // NB: There is no subgroup check for the G2 addition precompile. @@ -53,5 +54,5 @@ pub(super) fn g2_add(input: &Bytes, gas_limit: u64) -> PrecompileResult { unsafe { blst_p2_to_affine(&mut p_aff, &p) }; let out = encode_g2_point(&p_aff); - Ok((BASE_GAS_FEE, out)) + Ok(PrecompileOutput::new(BASE_GAS_FEE, out)) } diff --git a/crates/precompile/src/bls12_381/g2_msm.rs b/crates/precompile/src/bls12_381/g2_msm.rs index c59a93e220..456a66517b 100644 --- a/crates/precompile/src/bls12_381/g2_msm.rs +++ b/crates/precompile/src/bls12_381/g2_msm.rs @@ -6,7 +6,7 @@ use super::{ }; use crate::{u64_to_address, PrecompileWithAddress}; use blst::{blst_p2, blst_p2_affine, blst_p2_from_affine, blst_p2_to_affine, p2_affines}; -use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileResult}; +use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2MSM precompile. pub const PRECOMPILE: PrecompileWithAddress = @@ -30,13 +30,14 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { "G2MSM input length should be multiple of {}, was {}", g2_mul::INPUT_LENGTH, input_len - ))); + )) + .into()); } let k = input_len / g2_mul::INPUT_LENGTH; let required_gas = msm_required_gas(k, g2_mul::BASE_GAS_FEE); if required_gas > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } let mut g2_points: Vec = Vec::with_capacity(k); @@ -72,7 +73,7 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { // return infinity point if all points are infinity if g2_points.is_empty() { - return Ok((required_gas, [0; 256].into())); + return Ok(PrecompileOutput::new(required_gas, [0; 256].into())); } let points = p2_affines::from(&g2_points); @@ -83,5 +84,5 @@ pub(super) fn g2_msm(input: &Bytes, gas_limit: u64) -> PrecompileResult { unsafe { blst_p2_to_affine(&mut multiexp_aff, &multiexp) }; let out = encode_g2_point(&multiexp_aff); - Ok((required_gas, out)) + Ok(PrecompileOutput::new(required_gas, out)) } diff --git a/crates/precompile/src/bls12_381/g2_mul.rs b/crates/precompile/src/bls12_381/g2_mul.rs index 62cb903e9b..e39edf0751 100644 --- a/crates/precompile/src/bls12_381/g2_mul.rs +++ b/crates/precompile/src/bls12_381/g2_mul.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{u64_to_address, PrecompileWithAddress}; use blst::{blst_p2, blst_p2_affine, blst_p2_from_affine, blst_p2_mult, blst_p2_to_affine}; -use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileResult}; +use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_G2MUL precompile. pub const PRECOMPILE: PrecompileWithAddress = @@ -25,13 +25,14 @@ pub(super) const INPUT_LENGTH: usize = 288; /// See also: pub(super) fn g2_mul(input: &Bytes, gas_limit: u64) -> PrecompileResult { if BASE_GAS_FEE > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } if input.len() != INPUT_LENGTH { return Err(PrecompileError::Other(format!( "G2MUL input should be {INPUT_LENGTH} bytes, was {}", input.len() - ))); + )) + .into()); } // NB: Scalar multiplications, MSMs and pairings MUST perform a subgroup check. @@ -52,5 +53,5 @@ pub(super) fn g2_mul(input: &Bytes, gas_limit: u64) -> PrecompileResult { unsafe { blst_p2_to_affine(&mut p_aff, &p) }; let out = encode_g2_point(&p_aff); - Ok((BASE_GAS_FEE, out)) + Ok(PrecompileOutput::new(BASE_GAS_FEE, out)) } diff --git a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs index c914e844f8..584008ec62 100644 --- a/crates/precompile/src/bls12_381/map_fp2_to_g2.rs +++ b/crates/precompile/src/bls12_381/map_fp2_to_g2.rs @@ -5,7 +5,7 @@ use super::{ }; use crate::{u64_to_address, PrecompileWithAddress}; use blst::{blst_map_to_g2, blst_p2, blst_p2_affine, blst_p2_to_affine}; -use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileResult}; +use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_MAP_FP2_TO_G2 precompile. pub const PRECOMPILE: PrecompileWithAddress = @@ -23,14 +23,15 @@ const BASE_GAS_FEE: u64 = 75000; /// See also: pub(super) fn map_fp2_to_g2(input: &Bytes, gas_limit: u64) -> PrecompileResult { if BASE_GAS_FEE > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } if input.len() != PADDED_FP2_LENGTH { return Err(PrecompileError::Other(format!( "MAP_FP2_TO_G2 input should be {PADDED_FP2_LENGTH} bytes, was {}", input.len() - ))); + )) + .into()); } let input_p0_x = remove_padding(&input[..PADDED_FP_LENGTH])?; @@ -47,5 +48,5 @@ pub(super) fn map_fp2_to_g2(input: &Bytes, gas_limit: u64) -> PrecompileResult { unsafe { blst_p2_to_affine(&mut p_aff, &p) }; let out = encode_g2_point(&p_aff); - Ok((BASE_GAS_FEE, out)) + Ok(PrecompileOutput::new(BASE_GAS_FEE, out)) } diff --git a/crates/precompile/src/bls12_381/map_fp_to_g1.rs b/crates/precompile/src/bls12_381/map_fp_to_g1.rs index 26b5987322..b92df60a18 100644 --- a/crates/precompile/src/bls12_381/map_fp_to_g1.rs +++ b/crates/precompile/src/bls12_381/map_fp_to_g1.rs @@ -4,7 +4,7 @@ use super::{ }; use crate::{u64_to_address, PrecompileWithAddress}; use blst::{blst_map_to_g1, blst_p1, blst_p1_affine, blst_p1_to_affine}; -use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileResult}; +use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_MAP_FP_TO_G1 precompile. pub const PRECOMPILE: PrecompileWithAddress = @@ -21,14 +21,15 @@ const MAP_FP_TO_G1_BASE: u64 = 5500; /// See also: pub(super) fn map_fp_to_g1(input: &Bytes, gas_limit: u64) -> PrecompileResult { if MAP_FP_TO_G1_BASE > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } if input.len() != PADDED_FP_LENGTH { return Err(PrecompileError::Other(format!( "MAP_FP_TO_G1 input should be {PADDED_FP_LENGTH} bytes, was {}", input.len() - ))); + )) + .into()); } let input_p0 = remove_padding(input)?; @@ -44,7 +45,7 @@ pub(super) fn map_fp_to_g1(input: &Bytes, gas_limit: u64) -> PrecompileResult { unsafe { blst_p1_to_affine(&mut p_aff, &p) }; let out = encode_g1_point(&p_aff); - Ok((MAP_FP_TO_G1_BASE, out)) + Ok(PrecompileOutput::new(MAP_FP_TO_G1_BASE, out)) } #[cfg(test)] @@ -58,7 +59,7 @@ mod test { let fail = map_fp_to_g1(&input, MAP_FP_TO_G1_BASE); assert_eq!( fail, - Err(PrecompileError::Other("non-canonical fp value".to_string())) + Err(PrecompileError::Other("non-canonical fp value".to_string()).into()) ); } } diff --git a/crates/precompile/src/bls12_381/pairing.rs b/crates/precompile/src/bls12_381/pairing.rs index 55767e2e0e..e0c50dd834 100644 --- a/crates/precompile/src/bls12_381/pairing.rs +++ b/crates/precompile/src/bls12_381/pairing.rs @@ -4,7 +4,9 @@ use super::{ }; use crate::{u64_to_address, PrecompileWithAddress}; use blst::{blst_final_exp, blst_fp12, blst_fp12_is_one, blst_fp12_mul, blst_miller_loop}; -use revm_primitives::{Bytes, Precompile, PrecompileError, PrecompileResult, B256}; +use revm_primitives::{ + Bytes, Precompile, PrecompileError, PrecompileOutput, PrecompileResult, B256, +}; /// [EIP-2537](https://eips.ethereum.org/EIPS/eip-2537#specification) BLS12_PAIRING precompile. pub const PRECOMPILE: PrecompileWithAddress = @@ -34,13 +36,14 @@ pub(super) fn pairing(input: &Bytes, gas_limit: u64) -> PrecompileResult { if input_len == 0 || input_len % INPUT_LENGTH != 0 { return Err(PrecompileError::Other(format!( "Pairing input length should be multiple of {INPUT_LENGTH}, was {input_len}" - ))); + )) + .into()); } let k = input_len / INPUT_LENGTH; let required_gas: u64 = PAIRING_MULTIPLIER_BASE * k as u64 + PAIRING_OFFSET_BASE; if required_gas > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } // Accumulator for the fp12 multiplications of the miller loops. @@ -98,5 +101,8 @@ pub(super) fn pairing(input: &Bytes, gas_limit: u64) -> PrecompileResult { result = 1; } } - Ok((required_gas, B256::with_last_byte(result).into())) + Ok(PrecompileOutput::new( + required_gas, + B256::with_last_byte(result).into(), + )) } diff --git a/crates/precompile/src/bn128.rs b/crates/precompile/src/bn128.rs index ce08da7994..c9ce059d73 100644 --- a/crates/precompile/src/bn128.rs +++ b/crates/precompile/src/bn128.rs @@ -3,6 +3,7 @@ use crate::{ Address, Error, Precompile, PrecompileResult, PrecompileWithAddress, }; use bn::{AffineG1, AffineG2, Fq, Fq2, Group, Gt, G1, G2}; +use revm_primitives::PrecompileOutput; pub mod add { use super::*; @@ -122,7 +123,7 @@ pub fn new_g1_point(px: Fq, py: Fq) -> Result { pub fn run_add(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult { if gas_cost > gas_limit { - return Err(Error::OutOfGas); + return Err(Error::OutOfGas.into()); } let input = right_pad::(input); @@ -135,12 +136,12 @@ pub fn run_add(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult sum.x().to_big_endian(&mut output[..32]).unwrap(); sum.y().to_big_endian(&mut output[32..]).unwrap(); } - Ok((gas_cost, output.into())) + Ok(PrecompileOutput::new(gas_cost, output.into())) } pub fn run_mul(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult { if gas_cost > gas_limit { - return Err(Error::OutOfGas); + return Err(Error::OutOfGas.into()); } let input = right_pad::(input); @@ -155,7 +156,7 @@ pub fn run_mul(input: &[u8], gas_cost: u64, gas_limit: u64) -> PrecompileResult mul.x().to_big_endian(&mut output[..32]).unwrap(); mul.y().to_big_endian(&mut output[32..]).unwrap(); } - Ok((gas_cost, output.into())) + Ok(PrecompileOutput::new(gas_cost, output.into())) } pub fn run_pair( @@ -166,11 +167,11 @@ pub fn run_pair( ) -> PrecompileResult { let gas_used = (input.len() / PAIR_ELEMENT_LEN) as u64 * pair_per_point_cost + pair_base_cost; if gas_used > gas_limit { - return Err(Error::OutOfGas); + return Err(Error::OutOfGas.into()); } if input.len() % PAIR_ELEMENT_LEN != 0 { - return Err(Error::Bn128PairLength); + return Err(Error::Bn128PairLength.into()); } let success = if input.is_empty() { @@ -211,7 +212,7 @@ pub fn run_pair( mul == Gt::one() }; - Ok((gas_used, bool_to_bytes32(success))) + Ok(PrecompileOutput::new(gas_used, bool_to_bytes32(success))) } #[cfg(test)] @@ -219,7 +220,7 @@ mod tests { use crate::bn128::add::BYZANTIUM_ADD_GAS_COST; use crate::bn128::mul::BYZANTIUM_MUL_GAS_COST; use crate::bn128::pair::{BYZANTIUM_PAIR_BASE, BYZANTIUM_PAIR_PER_POINT}; - use revm_primitives::hex; + use revm_primitives::{hex, PrecompileErrors}; use super::*; @@ -240,8 +241,8 @@ mod tests { ) .unwrap(); - let (_, res) = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap(); - assert_eq!(res, expected); + let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap(); + assert_eq!(outcome.bytes, expected); // zero sum test let input = hex::decode( @@ -259,8 +260,8 @@ mod tests { ) .unwrap(); - let (_, res) = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap(); - assert_eq!(res, expected); + let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap(); + assert_eq!(outcome.bytes, expected); // out of gas test let input = hex::decode( @@ -274,7 +275,7 @@ mod tests { let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 499); println!("{:?}", res); - assert!(matches!(res, Err(Error::OutOfGas))); + assert!(matches!(res, Err(PrecompileErrors::Error(Error::OutOfGas)))); // no input test let input = [0u8; 0]; @@ -285,8 +286,8 @@ mod tests { ) .unwrap(); - let (_, res) = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap(); - assert_eq!(res, expected); + let outcome = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500).unwrap(); + assert_eq!(outcome.bytes, expected); // point not on curve fail let input = hex::decode( @@ -299,7 +300,10 @@ mod tests { .unwrap(); let res = run_add(&input, BYZANTIUM_ADD_GAS_COST, 500); - assert!(matches!(res, Err(Error::Bn128AffineGFailedToCreate))); + assert!(matches!( + res, + Err(PrecompileErrors::Error(Error::Bn128AffineGFailedToCreate)) + )); } #[test] @@ -318,8 +322,8 @@ mod tests { ) .unwrap(); - let (_, res) = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap(); - assert_eq!(res, expected); + let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap(); + assert_eq!(outcome.bytes, expected); // out of gas test let input = hex::decode( @@ -331,7 +335,7 @@ mod tests { .unwrap(); let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 39_999); - assert!(matches!(res, Err(Error::OutOfGas))); + assert!(matches!(res, Err(PrecompileErrors::Error(Error::OutOfGas)))); // zero multiplication test let input = hex::decode( @@ -348,8 +352,8 @@ mod tests { ) .unwrap(); - let (_, res) = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap(); - assert_eq!(res, expected); + let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap(); + assert_eq!(outcome.bytes, expected); // no input test let input = [0u8; 0]; @@ -360,8 +364,8 @@ mod tests { ) .unwrap(); - let (_, res) = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap(); - assert_eq!(res, expected); + let outcome = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000).unwrap(); + assert_eq!(outcome.bytes, expected); // point not on curve fail let input = hex::decode( @@ -373,7 +377,10 @@ mod tests { .unwrap(); let res = run_mul(&input, BYZANTIUM_MUL_GAS_COST, 40_000); - assert!(matches!(res, Err(Error::Bn128AffineGFailedToCreate))); + assert!(matches!( + res, + Err(PrecompileErrors::Error(Error::Bn128AffineGFailedToCreate)) + )); } #[test] @@ -398,14 +405,14 @@ mod tests { hex::decode("0000000000000000000000000000000000000000000000000000000000000001") .unwrap(); - let (_, res) = run_pair( + let outcome = run_pair( &input, BYZANTIUM_PAIR_PER_POINT, BYZANTIUM_PAIR_BASE, 260_000, ) .unwrap(); - assert_eq!(res, expected); + assert_eq!(outcome.bytes, expected); // out of gas test let input = hex::decode( @@ -431,7 +438,7 @@ mod tests { BYZANTIUM_PAIR_BASE, 259_999, ); - assert!(matches!(res, Err(Error::OutOfGas))); + assert!(matches!(res, Err(PrecompileErrors::Error(Error::OutOfGas)))); // no input test let input = [0u8; 0]; @@ -439,14 +446,14 @@ mod tests { hex::decode("0000000000000000000000000000000000000000000000000000000000000001") .unwrap(); - let (_, res) = run_pair( + let outcome = run_pair( &input, BYZANTIUM_PAIR_PER_POINT, BYZANTIUM_PAIR_BASE, 260_000, ) .unwrap(); - assert_eq!(res, expected); + assert_eq!(outcome.bytes, expected); // point not on curve fail let input = hex::decode( @@ -466,7 +473,10 @@ mod tests { BYZANTIUM_PAIR_BASE, 260_000, ); - assert!(matches!(res, Err(Error::Bn128AffineGFailedToCreate))); + assert!(matches!( + res, + Err(PrecompileErrors::Error(Error::Bn128AffineGFailedToCreate)) + )); // invalid input length let input = hex::decode( @@ -484,6 +494,9 @@ mod tests { BYZANTIUM_PAIR_BASE, 260_000, ); - assert!(matches!(res, Err(Error::Bn128PairLength))); + assert!(matches!( + res, + Err(PrecompileErrors::Error(Error::Bn128PairLength)) + )); } } diff --git a/crates/precompile/src/hash.rs b/crates/precompile/src/hash.rs index 9fc6a8ae49..9457302b1a 100644 --- a/crates/precompile/src/hash.rs +++ b/crates/precompile/src/hash.rs @@ -1,6 +1,6 @@ use super::calc_linear_cost_u32; use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress}; -use revm_primitives::Bytes; +use revm_primitives::{Bytes, PrecompileOutput}; use sha2::Digest; pub const SHA256: PrecompileWithAddress = @@ -17,10 +17,10 @@ pub const RIPEMD160: PrecompileWithAddress = PrecompileWithAddress( pub fn sha256_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let cost = calc_linear_cost_u32(input.len(), 60, 12); if cost > gas_limit { - Err(Error::OutOfGas) + Err(Error::OutOfGas.into()) } else { let output = sha2::Sha256::digest(input); - Ok((cost, output.to_vec().into())) + Ok(PrecompileOutput::new(cost, output.to_vec().into())) } } @@ -30,13 +30,13 @@ pub fn sha256_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { pub fn ripemd160_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let gas_used = calc_linear_cost_u32(input.len(), 600, 120); if gas_used > gas_limit { - Err(Error::OutOfGas) + Err(Error::OutOfGas.into()) } else { let mut hasher = ripemd::Ripemd160::new(); hasher.update(input); let mut output = [0u8; 32]; hasher.finalize_into((&mut output[12..]).into()); - Ok((gas_used, output.to_vec().into())) + Ok(PrecompileOutput::new(gas_used, output.to_vec().into())) } } diff --git a/crates/precompile/src/identity.rs b/crates/precompile/src/identity.rs index 85722ea810..ab869cd047 100644 --- a/crates/precompile/src/identity.rs +++ b/crates/precompile/src/identity.rs @@ -1,6 +1,6 @@ use super::calc_linear_cost_u32; use crate::{Error, Precompile, PrecompileResult, PrecompileWithAddress}; -use revm_primitives::Bytes; +use revm_primitives::{Bytes, PrecompileOutput}; pub const FUN: PrecompileWithAddress = PrecompileWithAddress(crate::u64_to_address(4), Precompile::Standard(identity_run)); @@ -17,7 +17,7 @@ pub const IDENTITY_PER_WORD: u64 = 3; pub fn identity_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let gas_used = calc_linear_cost_u32(input.len(), IDENTITY_BASE, IDENTITY_PER_WORD); if gas_used > gas_limit { - return Err(Error::OutOfGas); + return Err(Error::OutOfGas.into()); } - Ok((gas_used, input.clone())) + Ok(PrecompileOutput::new(gas_used, input.clone())) } diff --git a/crates/precompile/src/kzg_point_evaluation.rs b/crates/precompile/src/kzg_point_evaluation.rs index 5790186a99..22192ce56d 100644 --- a/crates/precompile/src/kzg_point_evaluation.rs +++ b/crates/precompile/src/kzg_point_evaluation.rs @@ -1,6 +1,6 @@ use crate::{Address, Error, Precompile, PrecompileResult, PrecompileWithAddress}; use c_kzg::{Bytes32, Bytes48, KzgProof, KzgSettings}; -use revm_primitives::{hex_literal::hex, Bytes, Env}; +use revm_primitives::{hex_literal::hex, Bytes, Env, PrecompileOutput}; use sha2::{Digest, Sha256}; pub const POINT_EVALUATION: PrecompileWithAddress = @@ -26,19 +26,19 @@ pub const RETURN_VALUE: &[u8; 64] = &hex!( /// with z and y being padded 32 byte big endian values pub fn run(input: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult { if gas_limit < GAS_COST { - return Err(Error::OutOfGas); + return Err(Error::OutOfGas.into()); } // Verify input length. if input.len() != 192 { - return Err(Error::BlobInvalidInputLength); + return Err(Error::BlobInvalidInputLength.into()); } // Verify commitment matches versioned_hash let versioned_hash = &input[..32]; let commitment = &input[96..144]; if kzg_to_versioned_hash(commitment) != versioned_hash { - return Err(Error::BlobMismatchedVersion); + return Err(Error::BlobMismatchedVersion.into()); } // Verify KZG proof with z and y in big endian format @@ -47,11 +47,11 @@ pub fn run(input: &Bytes, gas_limit: u64, env: &Env) -> PrecompileResult { let y = as_bytes32(&input[64..96]); let proof = as_bytes48(&input[144..192]); if !verify_kzg_proof(commitment, z, y, proof, env.cfg.kzg_settings.get()) { - return Err(Error::BlobVerifyKzgProofFailed); + return Err(Error::BlobVerifyKzgProofFailed.into()); } // Return FIELD_ELEMENTS_PER_BLOB and BLS_MODULUS as padded 32 byte big endian values - Ok((GAS_COST, RETURN_VALUE.into())) + Ok(PrecompileOutput::new(GAS_COST, RETURN_VALUE.into())) } /// `VERSIONED_HASH_VERSION_KZG ++ sha256(commitment)[1..]` @@ -113,8 +113,8 @@ mod tests { let expected_output = hex!("000000000000000000000000000000000000000000000000000000000000100073eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"); let gas = 50000; let env = Env::default(); - let (actual_gas, actual_output) = run(&input.into(), gas, &env).unwrap(); - assert_eq!(actual_gas, gas); - assert_eq!(actual_output[..], expected_output); + let output = run(&input.into(), gas, &env).unwrap(); + assert_eq!(output.gas_used, gas); + assert_eq!(output.bytes[..], expected_output); } } diff --git a/crates/precompile/src/lib.rs b/crates/precompile/src/lib.rs index a6c57a44a0..6f85a874d0 100644 --- a/crates/precompile/src/lib.rs +++ b/crates/precompile/src/lib.rs @@ -30,28 +30,12 @@ pub use revm_primitives::{ precompile::{PrecompileError as Error, *}, Address, Bytes, HashMap, Log, B256, }; -use std::{boxed::Box, vec::Vec}; +use std::boxed::Box; pub fn calc_linear_cost_u32(len: usize, base: u64, word: u64) -> u64 { (len as u64 + 32 - 1) / 32 * word + base } -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] -pub struct PrecompileOutput { - pub cost: u64, - pub output: Vec, - pub logs: Vec, -} - -impl PrecompileOutput { - pub fn without_logs(cost: u64, output: Vec) -> Self { - Self { - cost, - output, - logs: Vec::new(), - } - } -} #[derive(Clone, Default, Debug)] pub struct Precompiles { /// Precompiles. diff --git a/crates/precompile/src/modexp.rs b/crates/precompile/src/modexp.rs index a55b445912..8d9e5da8c0 100644 --- a/crates/precompile/src/modexp.rs +++ b/crates/precompile/src/modexp.rs @@ -5,7 +5,7 @@ use crate::{ }; use aurora_engine_modexp::modexp; use core::cmp::{max, min}; -use revm_primitives::Bytes; +use revm_primitives::{Bytes, PrecompileOutput}; pub const BYZANTIUM: PrecompileWithAddress = PrecompileWithAddress( crate::u64_to_address(5), @@ -50,7 +50,7 @@ where { // If there is no minimum gas, return error. if min_gas > gas_limit { - return Err(Error::OutOfGas); + return Err(Error::OutOfGas.into()); } // The format of input is: @@ -66,20 +66,20 @@ where // cast base and modulus to usize, it does not make sense to handle larger values let Ok(base_len) = usize::try_from(base_len) else { - return Err(Error::ModexpBaseOverflow); + return Err(Error::ModexpBaseOverflow.into()); }; let Ok(mod_len) = usize::try_from(mod_len) else { - return Err(Error::ModexpModOverflow); + return Err(Error::ModexpModOverflow.into()); }; // Handle a special case when both the base and mod length are zero. if base_len == 0 && mod_len == 0 { - return Ok((min_gas, Bytes::new())); + return Ok(PrecompileOutput::new(min_gas, Bytes::new())); } // Cast exponent length to usize, since it does not make sense to handle larger values. let Ok(exp_len) = usize::try_from(exp_len) else { - return Err(Error::ModexpModOverflow); + return Err(Error::ModexpModOverflow.into()); }; // Used to extract ADJUSTED_EXPONENT_LENGTH. @@ -99,7 +99,7 @@ where // Check if we have enough gas. let gas_cost = calc_gas(base_len as u64, exp_len as u64, mod_len as u64, &exp_highp); if gas_cost > gas_limit { - return Err(Error::OutOfGas); + return Err(Error::OutOfGas.into()); } // Padding is needed if the input does not contain all 3 values. @@ -113,7 +113,10 @@ where let output = modexp(base, exponent, modulus); // left pad the result to modulus length. bytes will always by less or equal to modulus length. - Ok((gas_cost, left_pad_vec(&output, mod_len).into_owned().into())) + Ok(PrecompileOutput::new( + gas_cost, + left_pad_vec(&output, mod_len).into_owned().into(), + )) } pub fn byzantium_gas_calc(base_len: u64, exp_len: u64, mod_len: u64, exp_highp: &U256) -> u64 { @@ -350,11 +353,11 @@ mod tests { let res = byzantium_run(&input, 100_000_000).unwrap(); let expected = hex::decode(test.expected).unwrap(); assert_eq!( - res.0, test_gas, + res.gas_used, test_gas, "used gas not matching for test: {}", test.name ); - assert_eq!(res.1, expected, "test:{}", test.name); + assert_eq!(res.bytes, expected, "test:{}", test.name); } } @@ -365,11 +368,11 @@ mod tests { let res = berlin_run(&input, 100_000_000).unwrap(); let expected = hex::decode(test.expected).unwrap(); assert_eq!( - res.0, test_gas, + res.gas_used, test_gas, "used gas not matching for test: {}", test.name ); - assert_eq!(res.1, expected, "test:{}", test.name); + assert_eq!(res.bytes, expected, "test:{}", test.name); } } @@ -377,6 +380,6 @@ mod tests { fn test_berlin_modexp_empty_input() { let res = berlin_run(&Bytes::new(), 100_000).unwrap(); let expected: Vec = Vec::new(); - assert_eq!(res.1, expected) + assert_eq!(res.bytes, expected) } } diff --git a/crates/precompile/src/secp256k1.rs b/crates/precompile/src/secp256k1.rs index 4ad079e1e7..86794b38db 100644 --- a/crates/precompile/src/secp256k1.rs +++ b/crates/precompile/src/secp256k1.rs @@ -1,5 +1,5 @@ use crate::{utilities::right_pad, Error, Precompile, PrecompileResult, PrecompileWithAddress}; -use revm_primitives::{alloy_primitives::B512, Bytes, B256}; +use revm_primitives::{alloy_primitives::B512, Bytes, PrecompileOutput, B256}; pub const ECRECOVER: PrecompileWithAddress = PrecompileWithAddress( crate::u64_to_address(1), @@ -70,14 +70,14 @@ pub fn ec_recover_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { const ECRECOVER_BASE: u64 = 3_000; if ECRECOVER_BASE > gas_limit { - return Err(Error::OutOfGas); + return Err(Error::OutOfGas.into()); } let input = right_pad::<128>(input); // `v` must be a 32-byte big-endian integer equal to 27 or 28. if !(input[32..63].iter().all(|&b| b == 0) && matches!(input[63], 27 | 28)) { - return Ok((ECRECOVER_BASE, Bytes::new())); + return Ok(PrecompileOutput::new(ECRECOVER_BASE, Bytes::new())); } let msg = <&B256>::try_from(&input[0..32]).unwrap(); @@ -87,5 +87,5 @@ pub fn ec_recover_run(input: &Bytes, gas_limit: u64) -> PrecompileResult { let out = secp256k1::ecrecover(sig, recid, msg) .map(|o| o.to_vec().into()) .unwrap_or_default(); - Ok((ECRECOVER_BASE, out)) + Ok(PrecompileOutput::new(ECRECOVER_BASE, out)) } diff --git a/crates/precompile/src/secp256r1.rs b/crates/precompile/src/secp256r1.rs index e2c9951a21..46f00b7405 100644 --- a/crates/precompile/src/secp256r1.rs +++ b/crates/precompile/src/secp256r1.rs @@ -8,7 +8,7 @@ //! with the address that it is currently deployed at. use crate::{u64_to_address, Precompile, PrecompileWithAddress}; use p256::ecdsa::{signature::hazmat::PrehashVerifier, Signature, VerifyingKey}; -use revm_primitives::{Bytes, PrecompileError, PrecompileResult, B256}; +use revm_primitives::{Bytes, PrecompileError, PrecompileOutput, PrecompileResult, B256}; /// Base gas fee for secp256r1 p256verify operation. const P256VERIFY_BASE: u64 = 3450; @@ -33,14 +33,14 @@ pub const P256VERIFY: PrecompileWithAddress = /// | 32 | 32 | 32 | 32 | 32 | pub fn p256_verify(input: &Bytes, gas_limit: u64) -> PrecompileResult { if P256VERIFY_BASE > gas_limit { - return Err(PrecompileError::OutOfGas); + return Err(PrecompileError::OutOfGas.into()); } let result = if verify_impl(input).is_some() { B256::with_last_byte(1).into() } else { Bytes::new() }; - Ok((P256VERIFY_BASE, result)) + Ok(PrecompileOutput::new(P256VERIFY_BASE, result)) } /// Returns `Some(())` if the signature included in the input byte slice is @@ -73,7 +73,7 @@ pub fn verify_impl(input: &[u8]) -> Option<()> { #[cfg(test)] mod test { use super::*; - use revm_primitives::hex::FromHex; + use crate::primitives::{hex::FromHex, PrecompileErrors}; use rstest::rstest; #[rstest] @@ -96,14 +96,14 @@ mod test { fn test_sig_verify(#[case] input: &str, #[case] expect_success: bool) { let input = Bytes::from_hex(input).unwrap(); let target_gas = 3_500u64; - let (gas_used, res) = p256_verify(&input, target_gas).unwrap(); - assert_eq!(gas_used, 3_450u64); + let outcome = p256_verify(&input, target_gas).unwrap(); + assert_eq!(outcome.gas_used, 3_450u64); let expected_result = if expect_success { B256::with_last_byte(1).into() } else { Bytes::new() }; - assert_eq!(res, expected_result); + assert_eq!(outcome.bytes, expected_result); } #[rstest] @@ -113,7 +113,10 @@ mod test { let result = p256_verify(&input, target_gas); assert!(result.is_err()); - assert_eq!(result.err(), Some(PrecompileError::OutOfGas)); + assert_eq!( + result.err(), + Some(PrecompileErrors::Error(PrecompileError::OutOfGas)) + ); } #[rstest] diff --git a/crates/primitives/src/precompile.rs b/crates/primitives/src/precompile.rs index e585377537..275244f849 100644 --- a/crates/primitives/src/precompile.rs +++ b/crates/primitives/src/precompile.rs @@ -1,12 +1,28 @@ use crate::{Bytes, Env}; -use core::fmt; +use core::fmt::{self}; use dyn_clone::DynClone; use std::{boxed::Box, string::String, sync::Arc}; /// A precompile operation result. /// /// Returns either `Ok((gas_used, return_bytes))` or `Err(error)`. -pub type PrecompileResult = Result<(u64, Bytes), PrecompileError>; +pub type PrecompileResult = Result; + +/// Precompile execution output +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct PrecompileOutput { + /// Gas used by the precompile. + pub gas_used: u64, + /// Output bytes. + pub bytes: Bytes, +} + +impl PrecompileOutput { + /// Returns new precompile output with the given gas used and output bytes. + pub fn new(gas_used: u64, bytes: Bytes) -> Self { + Self { gas_used, bytes } + } +} pub type StandardPrecompileFn = fn(&Bytes, u64) -> PrecompileResult; pub type EnvPrecompileFn = fn(&Bytes, u64, env: &Env) -> PrecompileResult; @@ -103,6 +119,25 @@ impl Precompile { } } +/// Precompile errors. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum PrecompileErrors { + Error(PrecompileError), + Fatal { msg: String }, +} + +#[cfg(feature = "std")] +impl std::error::Error for PrecompileErrors {} + +impl fmt::Display for PrecompileErrors { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Error(e) => e.fmt(f), + Self::Fatal { msg } => f.write_str(msg), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum PrecompileError { /// out of gas is the main error. Others are here just for completeness @@ -130,9 +165,21 @@ pub enum PrecompileError { } impl PrecompileError { + /// Returns an other error with the given message. pub fn other(err: impl Into) -> Self { Self::Other(err.into()) } + + /// Returns true if the error is out of gas. + pub fn is_oog(&self) -> bool { + matches!(self, Self::OutOfGas) + } +} + +impl From for PrecompileErrors { + fn from(err: PrecompileError) -> Self { + PrecompileErrors::Error(err) + } } #[cfg(feature = "std")] @@ -175,7 +222,7 @@ mod test { _gas_price: u64, _env: &Env, ) -> PrecompileResult { - PrecompileResult::Err(PrecompileError::OutOfGas) + Err(PrecompileError::OutOfGas.into()) } } diff --git a/crates/primitives/src/result.rs b/crates/primitives/src/result.rs index 36cd0db90d..28f35ce82c 100644 --- a/crates/primitives/src/result.rs +++ b/crates/primitives/src/result.rs @@ -148,6 +148,8 @@ pub enum EVMError { /// /// Useful for handler registers where custom logic would want to return their own custom error. Custom(String), + /// Precompile error. + Precompile(String), } #[cfg(feature = "std")] @@ -157,7 +159,7 @@ impl std::error::Error for EVMError Some(e), Self::Header(e) => Some(e), Self::Database(e) => Some(e), - Self::Custom(_) => None, + Self::Precompile(_) | Self::Custom(_) => None, } } } @@ -168,7 +170,7 @@ impl fmt::Display for EVMError { Self::Transaction(e) => write!(f, "transaction validation error: {e}"), Self::Header(e) => write!(f, "header validation error: {e}"), Self::Database(e) => write!(f, "database error: {e}"), - Self::Custom(e) => f.write_str(e), + Self::Precompile(e) | Self::Custom(e) => f.write_str(e), } } } diff --git a/crates/revm/src/builder.rs b/crates/revm/src/builder.rs index 4b2a81ec4f..a09f7c0bf5 100644 --- a/crates/revm/src/builder.rs +++ b/crates/revm/src/builder.rs @@ -449,6 +449,7 @@ mod test { Context, ContextPrecompile, ContextStatefulPrecompile, Evm, InMemoryDB, InnerEvmContext, }; use revm_interpreter::{gas, Host, Interpreter}; + use revm_precompile::PrecompileOutput; use std::{cell::RefCell, rc::Rc, sync::Arc}; /// Custom evm context @@ -614,7 +615,7 @@ mod test { _gas_price: u64, _context: &mut InnerEvmContext, ) -> PrecompileResult { - Ok((10, Bytes::new())) + Ok(PrecompileOutput::new(10, Bytes::new())) } } diff --git a/crates/revm/src/context/evm_context.rs b/crates/revm/src/context/evm_context.rs index b92d711da8..0cb969f6f6 100644 --- a/crates/revm/src/context/evm_context.rs +++ b/crates/revm/src/context/evm_context.rs @@ -1,4 +1,5 @@ use revm_interpreter::CallValue; +use revm_precompile::PrecompileErrors; use super::inner_evm_context::InnerEvmContext; use crate::{ @@ -107,10 +108,13 @@ impl EvmContext { address: Address, input_data: &Bytes, gas: Gas, - ) -> Option { - let out = self - .precompiles - .call(address, input_data, gas.limit(), &mut self.inner)?; + ) -> Result, EVMError> { + let Some(outcome) = + self.precompiles + .call(address, input_data, gas.limit(), &mut self.inner) + else { + return Ok(None); + }; let mut result = InterpreterResult { result: InstructionResult::Return, @@ -118,24 +122,25 @@ impl EvmContext { output: Bytes::new(), }; - match out { - Ok((gas_used, data)) => { - if result.gas.record_cost(gas_used) { + match outcome { + Ok(output) => { + if result.gas.record_cost(output.gas_used) { result.result = InstructionResult::Return; - result.output = data; + result.output = output.bytes; } else { result.result = InstructionResult::PrecompileOOG; } } - Err(e) => { - result.result = if e == crate::precompile::Error::OutOfGas { + Err(PrecompileErrors::Error(e)) => { + result.result = if e.is_oog() { InstructionResult::PrecompileOOG } else { InstructionResult::PrecompileError }; } + Err(PrecompileErrors::Fatal { msg }) => return Err(EVMError::Precompile(msg)), } - Some(result) + Ok(Some(result)) } /// Make call frame @@ -194,7 +199,7 @@ impl EvmContext { _ => {} }; - if let Some(result) = self.call_precompile(inputs.bytecode_address, &inputs.input, gas) { + if let Some(result) = self.call_precompile(inputs.bytecode_address, &inputs.input, gas)? { if matches!(result.result, return_ok!()) { self.journaled_state.checkpoint_commit(); } else { diff --git a/documentation/src/crates/interpreter/interpreter_action.md b/documentation/src/crates/interpreter/interpreter_action.md index 9654b43e16..2cc7599aea 100644 --- a/documentation/src/crates/interpreter/interpreter_action.md +++ b/documentation/src/crates/interpreter/interpreter_action.md @@ -1,6 +1,6 @@ # The `interpreter_action.rs` Module in the Rust Ethereum Virtual Machine (EVM) -The `interpreter_action.rs` module within this Rust EVM implementation encompasses a collection of datastructures used as internal models within the EVM. These models represent various aspects of EVM operations such as call and create inputs, call context, value transfers, and the result of self-destruction operations. +The `interpreter_action.rs` module within this Rust EVM implementation encompasses a collection of data structures used as internal models within the EVM. These models represent various aspects of EVM operations such as call and create inputs, call context, value transfers, and the result of self-destruction operations. ## Data Structures