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");