Assessment reports>Swisstronik>Discussion>The go-ethereum precompile tests

The go-ethereum precompile tests

The following test, to be added to the static-precompiles crate, adapts test cases from go-ethereum's test suite.

use evm::{
    interpreter::{
        error::ExitSucceed,
        runtime::{Context, RuntimeState, TransactionContext},
    },
    standard::{Config, GasometerState, State},
};
use primitive_types::{H160, U256};
use serde::Deserialize;
use std::rc::Rc;

use crate::{
    blake2f::Blake2F,
    bn128::{Bn128Add, Bn128Mul, Bn128Pairing},
    ec_recover::ECRecover,
    modexp::Modexp,
    Precompile,
};

#[derive(Deserialize)]
#[allow(dead_code, non_snake_case)]
struct GethPrecompileTestCase<'a> {
    Input: &'a [u8],
    Expected: &'a [u8],
    Name: &'a [u8],
    Gas: u64,
    NoBenchmark: bool,
}

#[derive(Deserialize)]
#[allow(dead_code, non_snake_case)]
struct GethPrecompileTestCaseFail<'a> {
    Input: &'a [u8],
    ExpectedError: &'a [u8],
    Name: &'a [u8],
}

fn test_state<'config>(config: &'config Config) -> State<'config> {
    let caller = H160::zero();
    let address = H160::zero();
    let value = U256::zero();
    let gas_price = U256::one();
    let context = Context {
        caller,
        address,
        apparent_value: value,
    };
    let transaction_context = TransactionContext {
        origin: caller,
        gas_price,
    };
    let runtime_state = RuntimeState {
        context,
        transaction_context: Rc::new(transaction_context),
        retbuf: Vec::new(),
    };
    State {
        runtime: runtime_state,
        gasometer: GasometerState::new(u64::MAX, false, &config),
    }
}

macro_rules! precompile_test {
    ($precompile:ident, $test_name:ident, $success:expr $(, $fail:expr )?) => {
        #[test]
        fn $test_name() {
            let testcases: Vec<GethPrecompileTestCase<'static>> =
                serde_json::from_slice(include_bytes!($success)).unwrap();
            for testcase in testcases {
                let input = hex::decode(&testcase.Input).unwrap();
                let expected = hex::decode(&testcase.Expected).unwrap();
                let config = Config::cancun();
                let mut state = test_state(&config);
                println!("{}", String::from_utf8_lossy(testcase.Name));
                let (result, output) = $precompile::execute(&input, &mut state);
                assert_eq!(result, Ok(ExitSucceed::Returned));
                assert_eq!(output, expected);
                if TEST_GAS {
                    assert_eq!(u64::MAX - state.gasometer.gas64(), testcase.Gas);
                }
            }
            $(
                let testcases: Vec<GethPrecompileTestCaseFail<'static>> =
                    serde_json::from_slice(include_bytes!($fail)).unwrap();
                for testcase in testcases {
                    let input = hex::decode(&testcase.Input).unwrap();
                    let config = Config::cancun();
                    let mut state = test_state(&config);
                    println!("{}", String::from_utf8_lossy(testcase.Name));
                    let (result, output) = $precompile::execute(&input, &mut state);
                    assert!(result.is_err());
                    assert!(output.is_empty());
            }
            )?
        }
    };
}

const TEST_GAS: bool = false;

precompile_test!(
    Blake2F,
    test_blake2f,
    "testdata/blake2F.json",
    "testdata/fail-blake2f.json"
);
precompile_test!(Bn128Add, test_bn256add, "testdata/bn256Add.json");
precompile_test!(Bn128Mul, test_bn256mul, "testdata/bn256ScalarMul.json");
precompile_test!(
    Bn128Pairing,
    test_bn256pairing,
    "testdata/bn256Pairing.json"
);
precompile_test!(ECRecover, test_ecrecover, "testdata/ecRecover.json");
precompile_test!(Modexp, test_modexp, "testdata/modexp_eip2565.json");
Zellic © 2025Back to top ↑