diff --git a/crates/rbuilder-primitives/src/evm_inspector.rs b/crates/rbuilder-primitives/src/evm_inspector.rs
index 82dff8287..f179c1d67 100644
--- a/crates/rbuilder-primitives/src/evm_inspector.rs
+++ b/crates/rbuilder-primitives/src/evm_inspector.rs
@@ -1,4 +1,4 @@
-use ahash::HashMap;
+use ahash::{HashMap, HashSet};
use alloy_consensus::Transaction;
use alloy_primitives::{Address, B256, U256};
use alloy_rpc_types::AccessList;
@@ -34,6 +34,8 @@ pub struct UsedStateTrace {
pub sent_amount: HashMap
,
pub created_contracts: Vec,
pub destructed_contracts: Vec,
+ /// addresses whose code was read via EXTCODEHASH, EXTCODESIZE, or EXTCODECOPY
+ pub read_code_addresses: HashSet,
}
impl UsedStateTrace {
@@ -74,6 +76,8 @@ impl UsedStateTrace {
}
self.destructed_contracts.push(*address);
}
+
+ self.read_code_addresses.extend(&other.read_code_addresses);
}
pub fn clear(&mut self) {
@@ -84,6 +88,7 @@ impl UsedStateTrace {
self.sent_amount.clear();
self.created_contracts.clear();
self.destructed_contracts.clear();
+ self.read_code_addresses.clear();
}
}
@@ -197,6 +202,12 @@ where
let addr = interpreter.input.target_address;
self.next_step_action = NextStepAction::ReadBalanceResult(addr);
}
+ opcode::EXTCODEHASH | opcode::EXTCODESIZE | opcode::EXTCODECOPY => {
+ if let Ok(addr) = interpreter.stack.peek(0) {
+ let addr = Address::from_word(B256::from(addr.to_be_bytes()));
+ self.used_state_trace.read_code_addresses.insert(addr);
+ }
+ }
_ => (),
}
}
diff --git a/crates/rbuilder/src/building/builders/parallel_builder/groups.rs b/crates/rbuilder/src/building/builders/parallel_builder/groups.rs
index f0ebede3c..a1f9f90c7 100644
--- a/crates/rbuilder/src/building/builders/parallel_builder/groups.rs
+++ b/crates/rbuilder/src/building/builders/parallel_builder/groups.rs
@@ -29,6 +29,7 @@ struct GroupData {
writes: Vec,
balance_reads: Vec,
balance_writes: Vec,
+ code_reads: Vec,
code_writes: Vec,
conflicting_group_ids: HashSet,
}
@@ -41,6 +42,7 @@ fn combine_groups(groups: Vec, removed_group_ids: Vec) -> Grou
let mut writes = Vec::default();
let mut balance_reads = Vec::default();
let mut balance_writes = Vec::default();
+ let mut code_reads = Vec::default();
let mut code_writes = Vec::default();
let mut conflicting_group_ids = removed_group_ids.into_iter().collect::>();
for group in groups {
@@ -49,6 +51,7 @@ fn combine_groups(groups: Vec, removed_group_ids: Vec) -> Grou
writes.extend(group.writes);
balance_reads.extend(group.balance_reads);
balance_writes.extend(group.balance_writes);
+ code_reads.extend(group.code_reads);
code_writes.extend(group.code_writes);
conflicting_group_ids.extend(group.conflicting_group_ids);
}
@@ -60,6 +63,8 @@ fn combine_groups(groups: Vec, removed_group_ids: Vec) -> Grou
balance_reads.dedup();
balance_writes.sort_unstable();
balance_writes.dedup();
+ code_reads.sort_unstable();
+ code_reads.dedup();
code_writes.sort_unstable();
code_writes.dedup();
@@ -69,6 +74,7 @@ fn combine_groups(groups: Vec, removed_group_ids: Vec) -> Grou
writes,
balance_reads,
balance_writes,
+ code_reads,
code_writes,
conflicting_group_ids,
}
@@ -82,6 +88,7 @@ pub struct ConflictFinder {
group_writes: HashMap>>, // same as above
group_balance_reads: HashMap>,
group_balance_writes: HashMap>,
+ group_code_reads: HashMap>,
group_code_writes: HashMap>,
groups: HashMap,
orders: HashSet,
@@ -95,6 +102,7 @@ impl ConflictFinder {
group_writes: HashMap::default(),
group_balance_reads: HashMap::default(),
group_balance_writes: HashMap::default(),
+ group_code_reads: HashMap::default(),
group_code_writes: HashMap::default(),
groups: HashMap::default(),
orders: HashSet::default(),
@@ -176,6 +184,16 @@ impl ConflictFinder {
let inner_groups = inner_mapping.values().flatten();
all_groups_in_conflict.extend(inner_groups);
}
+ // trying to create / destroy a contract other order is reading code from
+ if let Some(group) = self.group_code_reads.get(contract_addr) {
+ all_groups_in_conflict.extend_from_slice(group);
+ }
+ }
+ // reading code of a contract other order is creating / destroying
+ for code_read_addr in &used_state.read_code_addresses {
+ if let Some(group) = self.group_code_writes.get(code_read_addr) {
+ all_groups_in_conflict.extend_from_slice(group);
+ }
}
all_groups_in_conflict.sort();
all_groups_in_conflict.dedup();
@@ -198,12 +216,18 @@ impl ConflictFinder {
code_writes.sort_unstable();
code_writes.dedup();
+ let mut code_reads: Vec =
+ used_state.read_code_addresses.into_iter().collect();
+ code_reads.sort_unstable();
+ code_reads.dedup();
+
GroupData {
orders: vec![order],
reads: used_state.read_slot_values.into_keys().collect(),
writes: used_state.written_slot_values.into_keys().collect(),
balance_reads: used_state.read_balances.into_keys().collect(),
balance_writes,
+ code_reads,
code_writes,
conflicting_group_ids: HashSet::default(),
}
@@ -275,6 +299,12 @@ impl ConflictFinder {
&group_data.balance_writes,
&mut self.group_balance_writes,
);
+ add_group_to_map(
+ group_id,
+ is_new_id,
+ &group_data.code_reads,
+ &mut self.group_code_reads,
+ );
add_group_to_map(
group_id,
is_new_id,
@@ -302,6 +332,7 @@ impl ConflictFinder {
&group_data.balance_writes,
&mut self.group_balance_writes,
);
+ remove_group_from_map(group_id, &group_data.code_reads, &mut self.group_code_reads);
remove_group_from_map(
group_id,
&group_data.code_writes,
@@ -440,6 +471,27 @@ mod tests {
balance_write: Option<&Address>,
contract_creation: Option<&Address>,
contract_destruction: Option<&Address>,
+ ) -> Arc {
+ self.create_order_with_code_read(
+ read,
+ write,
+ balance_read,
+ balance_write,
+ contract_creation,
+ contract_destruction,
+ None,
+ )
+ }
+
+ pub fn create_order_with_code_read(
+ &mut self,
+ read: Option<&SlotKey>,
+ write: Option<&SlotKey>,
+ balance_read: Option<&Address>,
+ balance_write: Option<&Address>,
+ contract_creation: Option<&Address>,
+ contract_destruction: Option<&Address>,
+ code_read: Option<&Address>,
) -> Arc {
let mut trace = UsedStateTrace::default();
if let Some(read) = read {
@@ -469,6 +521,9 @@ mod tests {
if let Some(contract_address) = contract_destruction {
trace.destructed_contracts.push(*contract_address);
}
+ if let Some(code_read_addr) = code_read {
+ trace.read_code_addresses.insert(*code_read_addr);
+ }
Arc::new(SimulatedOrder::new(
Arc::new(Order::Tx(MempoolTx {
@@ -595,4 +650,72 @@ mod tests {
let groups = cached_groups.get_order_groups();
assert_eq!(groups.len(), 1);
}
+
+ #[test]
+ fn two_code_reads_no_conflict() {
+ let mut data_gen = DataGenerator::new();
+ let addr = Address::random();
+ let oa =
+ data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
+ let ob =
+ data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
+ let mut cached_groups = ConflictFinder::new();
+ cached_groups.add_orders(vec![oa, ob]);
+ let groups = cached_groups.get_order_groups();
+ assert_eq!(groups.len(), 2);
+ }
+
+ #[test]
+ fn code_read_and_creation_conflict() {
+ let mut data_gen = DataGenerator::new();
+ let addr = Address::random();
+ let oa =
+ data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
+ let ob = data_gen.create_order(None, None, None, None, Some(&addr), None);
+ let mut cached_groups = ConflictFinder::new();
+ cached_groups.add_orders(vec![oa, ob]);
+ let groups = cached_groups.get_order_groups();
+ assert_eq!(groups.len(), 1);
+ }
+
+ #[test]
+ fn code_read_and_destruction_conflict() {
+ let mut data_gen = DataGenerator::new();
+ let addr = Address::random();
+ let oa =
+ data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
+ let ob = data_gen.create_order(None, None, None, None, None, Some(&addr));
+ let mut cached_groups = ConflictFinder::new();
+ cached_groups.add_orders(vec![oa, ob]);
+ let groups = cached_groups.get_order_groups();
+ assert_eq!(groups.len(), 1);
+ }
+
+ #[test]
+ fn creation_then_code_read_conflict() {
+ let mut data_gen = DataGenerator::new();
+ let addr = Address::random();
+ // code_write first, then code_read — tests the reverse direction
+ let oa = data_gen.create_order(None, None, None, None, Some(&addr), None);
+ let ob =
+ data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
+ let mut cached_groups = ConflictFinder::new();
+ cached_groups.add_orders(vec![oa, ob]);
+ let groups = cached_groups.get_order_groups();
+ assert_eq!(groups.len(), 1);
+ }
+
+ #[test]
+ fn destruction_then_code_read_conflict() {
+ let mut data_gen = DataGenerator::new();
+ let addr = Address::random();
+ // code_write first, then code_read — tests the reverse direction
+ let oa = data_gen.create_order(None, None, None, None, None, Some(&addr));
+ let ob =
+ data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
+ let mut cached_groups = ConflictFinder::new();
+ cached_groups.add_orders(vec![oa, ob]);
+ let groups = cached_groups.get_order_groups();
+ assert_eq!(groups.len(), 1);
+ }
}
diff --git a/crates/rbuilder/src/building/testing/contracts.json b/crates/rbuilder/src/building/testing/contracts.json
index 7816e7bd1..7a8c27ced 100644
--- a/crates/rbuilder/src/building/testing/contracts.json
+++ b/crates/rbuilder/src/building/testing/contracts.json
@@ -1,4 +1,4 @@
{
- "MevTest": "0x6080604052600436106100555760003560e01c80634988880a1461005a5780637da3c3ab1461006f578063d6782ec714610077578063e6d252451461008a578063f9711c221461009d578063f9da581d146100b0575b600080fd5b61006d61006836600461027b565b6100b8565b005b61006d610159565b61006d61008536600461029d565b610163565b61006d61009836600461029d565b61017b565b61006d6100ab36600461029d565b6101b0565b61006d610240565b815481811461010d5760405162461bcd60e51b815260206004820152601860248201527f4f6c642076616c756520646f6573206e6f74206d617463680000000000000000604482015260640160405180910390fd5b600061011a8360016102cd565b808555905034156101535760405141903480156108fc02916000818181858888f19350505050158015610151573d6000803e3d6000fd5b505b50505050565b610161610159565b565b6101776001600160a01b03821631476102cd565b5050565b6040516001600160a01b038216903480156108fc02916000818181858888f19350505050158015610177573d6000803e3d6000fd5b60006040516101be9061026f565b604051809103906000f0801580156101da573d6000803e3d6000fd5b50604051631beb261560e01b81526001600160a01b03848116600483015291925090821690631beb26159034906024016000604051808303818588803b15801561022357600080fd5b505af1158015610237573d6000803e3d6000fd5b50505050505050565b60405141903480156108fc02916000818181858888f1935050505015801561026c573d6000803e3d6000fd5b50565b60bf806102f583390190565b6000806040838503121561028e57600080fd5b50508035926020909101359150565b6000602082840312156102af57600080fd5b81356001600160a01b03811681146102c657600080fd5b9392505050565b808201808211156102ee57634e487b7160e01b600052601160045260246000fd5b9291505056fe6080604052348015600f57600080fd5b5060a280601d6000396000f3fe608060405260043610601c5760003560e01c80631beb2615146021575b600080fd5b6030602c366004603e565b6032565b005b806001600160a01b0316ff5b600060208284031215604f57600080fd5b81356001600160a01b0381168114606557600080fd5b939250505056fea26469706673582212207422a0f368426edbe9d06fc472e76995dc7edc7b9e20673c4ab45757ae32f30064736f6c634300081a0033a26469706673582212201b4bf34a5948cd633421a6c6648a0db2ec7df1d12ccf2973d2cf78dd6b5775e464736f6c634300081a0033",
- "MevTestInitBytecode": "0x6080604052348015600f57600080fd5b506103e98061001f6000396000f3fe6080604052600436106100555760003560e01c80634988880a1461005a5780637da3c3ab1461006f578063d6782ec714610077578063e6d252451461008a578063f9711c221461009d578063f9da581d146100b0575b600080fd5b61006d61006836600461027b565b6100b8565b005b61006d610159565b61006d61008536600461029d565b610163565b61006d61009836600461029d565b61017b565b61006d6100ab36600461029d565b6101b0565b61006d610240565b815481811461010d5760405162461bcd60e51b815260206004820152601860248201527f4f6c642076616c756520646f6573206e6f74206d617463680000000000000000604482015260640160405180910390fd5b600061011a8360016102cd565b808555905034156101535760405141903480156108fc02916000818181858888f19350505050158015610151573d6000803e3d6000fd5b505b50505050565b610161610159565b565b6101776001600160a01b03821631476102cd565b5050565b6040516001600160a01b038216903480156108fc02916000818181858888f19350505050158015610177573d6000803e3d6000fd5b60006040516101be9061026f565b604051809103906000f0801580156101da573d6000803e3d6000fd5b50604051631beb261560e01b81526001600160a01b03848116600483015291925090821690631beb26159034906024016000604051808303818588803b15801561022357600080fd5b505af1158015610237573d6000803e3d6000fd5b50505050505050565b60405141903480156108fc02916000818181858888f1935050505015801561026c573d6000803e3d6000fd5b50565b60bf806102f583390190565b6000806040838503121561028e57600080fd5b50508035926020909101359150565b6000602082840312156102af57600080fd5b81356001600160a01b03811681146102c657600080fd5b9392505050565b808201808211156102ee57634e487b7160e01b600052601160045260246000fd5b9291505056fe6080604052348015600f57600080fd5b5060a280601d6000396000f3fe608060405260043610601c5760003560e01c80631beb2615146021575b600080fd5b6030602c366004603e565b6032565b005b806001600160a01b0316ff5b600060208284031215604f57600080fd5b81356001600160a01b0381168114606557600080fd5b939250505056fea26469706673582212207422a0f368426edbe9d06fc472e76995dc7edc7b9e20673c4ab45757ae32f30064736f6c634300081a0033a26469706673582212201b4bf34a5948cd633421a6c6648a0db2ec7df1d12ccf2973d2cf78dd6b5775e464736f6c634300081a0033"
+ "MevTest": "0x60806040526004361061006f575f3560e01c8063d6782ec71161004d578063d6782ec7146100d6578063e6d25245146100f2578063f9711c221461010e578063f9da581d1461012a5761006f565b80634988880a146100735780637da3c3ab1461008f578063c507b59214610099575b5f5ffd5b61008d60048036038101906100889190610387565b610134565b005b6100976101e1565b005b3480156100a4575f5ffd5b506100bf60048036038101906100ba919061041f565b6101eb565b6040516100cd929190610471565b60405180910390f35b6100f060048036038101906100eb91906104d3565b6101fa565b005b61010c600480360381019061010791906104d3565b610221565b005b610128600480360381019061012391906104d3565b610268565b005b6101326102fd565b005b5f8254905081811461017b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161017290610558565b60405180910390fd5b5f60018361018991906105a3565b90508084555f3411156101db574173ffffffffffffffffffffffffffffffffffffffff166108fc3490811502906040515f60405180830381858888f193505050501580156101d9573d5f5f3e3d5ffd5b505b50505050565b6101e96101e1565b565b5f5f823f9150823b9050915091565b8073ffffffffffffffffffffffffffffffffffffffff16314761021d91906105a3565b5050565b8073ffffffffffffffffffffffffffffffffffffffff166108fc3490811502906040515f60405180830381858888f19350505050158015610264573d5f5f3e3d5ffd5b5050565b5f60405161027590610343565b604051809103905ff08015801561028e573d5f5f3e3d5ffd5b5090508073ffffffffffffffffffffffffffffffffffffffff16631beb261534846040518363ffffffff1660e01b81526004016102cb91906105e5565b5f604051808303818588803b1580156102e2575f5ffd5b505af11580156102f4573d5f5f3e3d5ffd5b50505050505050565b4173ffffffffffffffffffffffffffffffffffffffff166108fc3490811502906040515f60405180830381858888f19350505050158015610340573d5f5f3e3d5ffd5b50565b61011f806105ff83390190565b5f5ffd5b5f819050919050565b61036681610354565b8114610370575f5ffd5b50565b5f813590506103818161035d565b92915050565b5f5f6040838503121561039d5761039c610350565b5b5f6103aa85828601610373565b92505060206103bb85828601610373565b9150509250929050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6103ee826103c5565b9050919050565b6103fe816103e4565b8114610408575f5ffd5b50565b5f81359050610419816103f5565b92915050565b5f6020828403121561043457610433610350565b5b5f6104418482850161040b565b91505092915050565b5f819050919050565b61045c8161044a565b82525050565b61046b81610354565b82525050565b5f6040820190506104845f830185610453565b6104916020830184610462565b9392505050565b5f6104a2826103c5565b9050919050565b6104b281610498565b81146104bc575f5ffd5b50565b5f813590506104cd816104a9565b92915050565b5f602082840312156104e8576104e7610350565b5b5f6104f5848285016104bf565b91505092915050565b5f82825260208201905092915050565b7f4f6c642076616c756520646f6573206e6f74206d6174636800000000000000005f82015250565b5f6105426018836104fe565b915061054d8261050e565b602082019050919050565b5f6020820190508181035f83015261056f81610536565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6105ad82610354565b91506105b883610354565b92508282019050808211156105d0576105cf610576565b5b92915050565b6105df81610498565b82525050565b5f6020820190506105f85f8301846105d6565b9291505056fe6080604052348015600e575f5ffd5b506101038061001c5f395ff3fe608060405260043610601b575f3560e01c80631beb261514601f575b5f5ffd5b603560048036038101906031919060a7565b6037565b005b8073ffffffffffffffffffffffffffffffffffffffff16ff5b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f607b826054565b9050919050565b6089816073565b81146092575f5ffd5b50565b5f8135905060a1816082565b92915050565b5f6020828403121560b95760b86050565b5b5f60c4848285016095565b9150509291505056fea264697066735822122046046525505278e63569492562d418a9cee99cfa26817db4080edd9b928aed4f64736f6c63430008210033a2646970667358221220d492e479b75edcd03de33e0e0d5a0e2fcd05a4092f6b2cea7c4e7b75497566e864736f6c63430008210033",
+ "MevTestInitBytecode": "0x6080604052348015600e575f5ffd5b506107538061001c5f395ff3fe60806040526004361061006f575f3560e01c8063d6782ec71161004d578063d6782ec7146100d6578063e6d25245146100f2578063f9711c221461010e578063f9da581d1461012a5761006f565b80634988880a146100735780637da3c3ab1461008f578063c507b59214610099575b5f5ffd5b61008d60048036038101906100889190610387565b610134565b005b6100976101e1565b005b3480156100a4575f5ffd5b506100bf60048036038101906100ba919061041f565b6101eb565b6040516100cd929190610471565b60405180910390f35b6100f060048036038101906100eb91906104d3565b6101fa565b005b61010c600480360381019061010791906104d3565b610221565b005b610128600480360381019061012391906104d3565b610268565b005b6101326102fd565b005b5f8254905081811461017b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161017290610558565b60405180910390fd5b5f60018361018991906105a3565b90508084555f3411156101db574173ffffffffffffffffffffffffffffffffffffffff166108fc3490811502906040515f60405180830381858888f193505050501580156101d9573d5f5f3e3d5ffd5b505b50505050565b6101e96101e1565b565b5f5f823f9150823b9050915091565b8073ffffffffffffffffffffffffffffffffffffffff16314761021d91906105a3565b5050565b8073ffffffffffffffffffffffffffffffffffffffff166108fc3490811502906040515f60405180830381858888f19350505050158015610264573d5f5f3e3d5ffd5b5050565b5f60405161027590610343565b604051809103905ff08015801561028e573d5f5f3e3d5ffd5b5090508073ffffffffffffffffffffffffffffffffffffffff16631beb261534846040518363ffffffff1660e01b81526004016102cb91906105e5565b5f604051808303818588803b1580156102e2575f5ffd5b505af11580156102f4573d5f5f3e3d5ffd5b50505050505050565b4173ffffffffffffffffffffffffffffffffffffffff166108fc3490811502906040515f60405180830381858888f19350505050158015610340573d5f5f3e3d5ffd5b50565b61011f806105ff83390190565b5f5ffd5b5f819050919050565b61036681610354565b8114610370575f5ffd5b50565b5f813590506103818161035d565b92915050565b5f5f6040838503121561039d5761039c610350565b5b5f6103aa85828601610373565b92505060206103bb85828601610373565b9150509250929050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6103ee826103c5565b9050919050565b6103fe816103e4565b8114610408575f5ffd5b50565b5f81359050610419816103f5565b92915050565b5f6020828403121561043457610433610350565b5b5f6104418482850161040b565b91505092915050565b5f819050919050565b61045c8161044a565b82525050565b61046b81610354565b82525050565b5f6040820190506104845f830185610453565b6104916020830184610462565b9392505050565b5f6104a2826103c5565b9050919050565b6104b281610498565b81146104bc575f5ffd5b50565b5f813590506104cd816104a9565b92915050565b5f602082840312156104e8576104e7610350565b5b5f6104f5848285016104bf565b91505092915050565b5f82825260208201905092915050565b7f4f6c642076616c756520646f6573206e6f74206d6174636800000000000000005f82015250565b5f6105426018836104fe565b915061054d8261050e565b602082019050919050565b5f6020820190508181035f83015261056f81610536565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b5f6105ad82610354565b91506105b883610354565b92508282019050808211156105d0576105cf610576565b5b92915050565b6105df81610498565b82525050565b5f6020820190506105f85f8301846105d6565b9291505056fe6080604052348015600e575f5ffd5b506101038061001c5f395ff3fe608060405260043610601b575f3560e01c80631beb261514601f575b5f5ffd5b603560048036038101906031919060a7565b6037565b005b8073ffffffffffffffffffffffffffffffffffffffff16ff5b5f5ffd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f607b826054565b9050919050565b6089816073565b81146092575f5ffd5b50565b5f8135905060a1816082565b92915050565b5f6020828403121560b95760b86050565b5b5f60c4848285016095565b9150509291505056fea264697066735822122046046525505278e63569492562d418a9cee99cfa26817db4080edd9b928aed4f64736f6c63430008210033a2646970667358221220d492e479b75edcd03de33e0e0d5a0e2fcd05a4092f6b2cea7c4e7b75497566e864736f6c63430008210033"
}
diff --git a/crates/rbuilder/src/building/testing/evm_inspector_tests/mod.rs b/crates/rbuilder/src/building/testing/evm_inspector_tests/mod.rs
index fe3f384c8..ed1933533 100644
--- a/crates/rbuilder/src/building/testing/evm_inspector_tests/mod.rs
+++ b/crates/rbuilder/src/building/testing/evm_inspector_tests/mod.rs
@@ -154,3 +154,33 @@ fn test_ephemeral_contract_destruct() -> eyre::Result<()> {
Ok(())
}
+
+#[test]
+fn test_read_code() -> eyre::Result<()> {
+ let test_setup = TestSetup::new()?;
+
+ let mev_test_contract_addr = test_setup.named_address(NamedAddr::MevTest)?;
+ let dummy_addr = test_setup.named_address(NamedAddr::Dummy)?;
+
+ // read code of MevTest contract (has code)
+ let tx = test_setup.make_test_read_code_tx(mev_test_contract_addr)?;
+ let used_state_trace = test_setup.inspect_tx_without_commit(tx)?;
+
+ assert!(
+ used_state_trace
+ .read_code_addresses
+ .contains(&mev_test_contract_addr),
+ "should track EXTCODEHASH/EXTCODESIZE on contract with code"
+ );
+
+ // read code of dummy address (no code)
+ let tx = test_setup.make_test_read_code_tx(dummy_addr)?;
+ let used_state_trace = test_setup.inspect_tx_without_commit(tx)?;
+
+ assert!(
+ used_state_trace.read_code_addresses.contains(&dummy_addr),
+ "should track EXTCODEHASH/EXTCODESIZE on address without code"
+ );
+
+ Ok(())
+}
diff --git a/crates/rbuilder/src/building/testing/evm_inspector_tests/setup.rs b/crates/rbuilder/src/building/testing/evm_inspector_tests/setup.rs
index 07f536879..001b5ec7f 100644
--- a/crates/rbuilder/src/building/testing/evm_inspector_tests/setup.rs
+++ b/crates/rbuilder/src/building/testing/evm_inspector_tests/setup.rs
@@ -68,6 +68,15 @@ impl TestSetup {
Ok(tx)
}
+ pub fn make_test_read_code_tx(
+ &self,
+ target: Address,
+ ) -> eyre::Result> {
+ let tx_args = TxArgs::new_test_read_code(NamedAddr::User(0), 0, target);
+ let tx = self.test_chain.sign_tx(tx_args)?;
+ Ok(tx)
+ }
+
pub fn make_test_ephemeral_contract_destruct_tx(
&self,
refund_addr: Address,
diff --git a/crates/rbuilder/src/building/testing/test_chain_state.rs b/crates/rbuilder/src/building/testing/test_chain_state.rs
index 11ac87655..323dd7298 100644
--- a/crates/rbuilder/src/building/testing/test_chain_state.rs
+++ b/crates/rbuilder/src/building/testing/test_chain_state.rs
@@ -508,6 +508,17 @@ impl TxArgs {
.value(value)
}
+ /// This transaction for test purpose only, it reads the code hash and size of the given address.
+ pub fn new_test_read_code(from: NamedAddr, nonce: u64, target: Address) -> Self {
+ Self::new(from, nonce).to(NamedAddr::MevTest).input(
+ [
+ (*TEST_READ_CODE).into(),
+ B256::left_padding_from(target.as_slice()).to_vec(),
+ ]
+ .concat(),
+ )
+ }
+
/// This transaction for test purpose only, it deploys a contract and let it selfdestruct within the tx.
pub fn new_test_ephemeral_contract_destruct(
from: NamedAddr,
@@ -587,6 +598,7 @@ lazy_static! {
static ref TEST_READ_BALANCE: [u8; 4] = selector("testReadBalance(address)");
static ref TEST_EPHEMERAL_CONTRACT_DESTRUCT: [u8; 4] =
selector("testEphemeralContractDestruct(address)");
+ static ref TEST_READ_CODE: [u8; 4] = selector("testReadCode(address)");
}
impl TestContracts {
diff --git a/mev-test-contract/src/MevTest.sol b/mev-test-contract/src/MevTest.sol
index 9b05603c6..e898df559 100644
--- a/mev-test-contract/src/MevTest.sol
+++ b/mev-test-contract/src/MevTest.sol
@@ -53,4 +53,12 @@ contract MevTest {
EphemeralContractTest ephemeral_contract = new EphemeralContractTest();
ephemeral_contract.destruct{value: msg.value}(refund);
}
+
+ /// Reads the code hash and code size of the given address, for testing evm inspector with extcodehash/extcodesize opcodes.
+ function testReadCode(address target) public view returns (bytes32 h, uint256 s) {
+ assembly {
+ h := extcodehash(target)
+ s := extcodesize(target)
+ }
+ }
}