use alloy_primitives::{address, Bytes, TxKind, U256};
use mega_evm::{
test_utils::{BytecodeBuilder, ErrorInjectingDatabase, InjectedDbError, MemoryDatabase},
EVMError, MegaContext, MegaEvm, MegaHaltReason, MegaSpecId, MegaTransaction,
MegaTransactionError,
};
use revm::{
bytecode::opcode::CALL,
context::{result::ResultAndState, TxEnv},
primitives::Address,
};
const CALLER: Address = address!("2000000000000000000000000000000000000002");
const CALLEE: Address = address!("1000000000000000000000000000000000000001");
const TARGET: Address = address!("3000000000000000000000000000000000000003");
fn transact(
spec: MegaSpecId,
db: ErrorInjectingDatabase,
caller: Address,
callee: Option<Address>,
data: Bytes,
value: U256,
gas_limit: u64,
) -> Result<ResultAndState<MegaHaltReason>, EVMError<InjectedDbError, MegaTransactionError>> {
let mut context = MegaContext::new(db, spec);
context.modify_chain(|chain| {
chain.operator_fee_scalar = Some(U256::from(0));
chain.operator_fee_constant = Some(U256::from(0));
});
let mut evm = MegaEvm::new(context);
let tx = TxEnv {
caller,
kind: callee.map_or(TxKind::Create, TxKind::Call),
data,
value,
gas_limit,
..Default::default()
};
let mut tx = MegaTransaction::new(tx);
tx.enveloped_tx = Some(Bytes::new());
alloy_evm::Evm::transact_raw(&mut evm, tx)
}
#[test]
fn test_sstore_db_error_on_inspect_storage() {
let storage_key = U256::from(0x42);
let storage_value = U256::from(0x1);
let bytecode = BytecodeBuilder::default().sstore(storage_key, storage_value).stop().build();
let mut inner_db = MemoryDatabase::default();
inner_db.set_account_balance(CALLER, U256::from(100_000_000_000u64));
inner_db.set_account_code(CALLEE, bytecode);
let mut db = ErrorInjectingDatabase::new(inner_db);
db.fail_on_storage = Some((CALLEE, storage_key));
let result = transact(
MegaSpecId::MINI_REX,
db,
CALLER,
Some(CALLEE),
Bytes::new(),
U256::ZERO,
1_000_000,
);
match result {
Err(EVMError::Custom(msg)) => {
assert!(
msg.contains("injected storage()"),
"error message should contain injected error, got: {msg}"
);
}
Err(other) => panic!("expected EVMError::Custom, got: {other:?}"),
Ok(result) => panic!("expected error, got success: {:?}", result.result),
}
}
#[test]
fn test_call_with_transfer_db_error_on_inspect_account() {
let bytecode = BytecodeBuilder::default()
.push_number(0_u64) .push_number(0_u64) .push_number(0_u64) .push_number(0_u64) .push_number(1_u64) .push_address(TARGET)
.push_number(100_000_u64) .append(CALL)
.stop()
.build();
let mut inner_db = MemoryDatabase::default();
inner_db.set_account_balance(CALLER, U256::from(100_000_000_000u64));
inner_db.set_account_code(CALLEE, bytecode);
let mut db = ErrorInjectingDatabase::new(inner_db);
db.fail_on_account = Some(TARGET);
let result = transact(
MegaSpecId::MINI_REX,
db,
CALLER,
Some(CALLEE),
Bytes::new(),
U256::ZERO,
1_000_000,
);
match result {
Err(EVMError::Custom(msg)) => {
assert!(
msg.contains("injected basic()"),
"error message should contain injected error, got: {msg}"
);
}
Err(other) => panic!("expected EVMError::Custom, got: {other:?}"),
Ok(result) => panic!("expected error, got success: {:?}", result.result),
}
}
#[test]
fn test_inspect_storage_skips_db_for_newly_created_account() {
let initcode = BytecodeBuilder::default().sstore(U256::ZERO, U256::from(0x42)).stop().build();
let created = CALLER.create(0);
let mut inner_db = MemoryDatabase::default();
inner_db.set_account_balance(CALLER, U256::from(100_000_000_000u64));
inner_db.set_account_balance(created, U256::from(1));
let mut db = ErrorInjectingDatabase::new(inner_db);
db.fail_on_storage = Some((created, U256::ZERO));
let result = transact(MegaSpecId::MINI_REX, db, CALLER, None, initcode, U256::ZERO, 10_000_000);
let res = result.expect("CREATE should not surface a DB error");
assert!(
res.result.is_success(),
"CREATE should succeed without DB::storage being queried, got: {:?}",
res.result
);
}
#[test]
fn test_staticcall_db_error_on_inspect_account() {
let bytecode = BytecodeBuilder::default()
.push_number(0_u64) .push_number(0_u64) .push_number(0_u64) .push_number(0_u64) .push_address(TARGET) .push_number(100_000_u64) .append(revm::bytecode::opcode::STATICCALL)
.stop()
.build();
let mut inner_db = MemoryDatabase::default();
inner_db.set_account_balance(CALLER, U256::from(100_000_000_000u64));
inner_db.set_account_code(CALLEE, bytecode);
let mut db = ErrorInjectingDatabase::new(inner_db);
db.fail_on_account = Some(TARGET);
let result =
transact(MegaSpecId::REX, db, CALLER, Some(CALLEE), Bytes::new(), U256::ZERO, 1_000_000);
match result {
Err(EVMError::Custom(msg)) => {
assert!(
msg.contains("injected basic()"),
"error message should contain injected error, got: {msg}"
);
}
Err(other) => panic!("expected EVMError::Custom, got: {other:?}"),
Ok(result) => panic!("expected error, got success: {:?}", result.result),
}
}