Skip to content

Commit 02ece48

Browse files
ju0xMars42
authored andcommitted
feat: track EXTCODEHASH, EXTCODESIZE, EXTCODECOPY in UsedStateEVMInspector
Closes #58
1 parent 8365e13 commit 02ece48

7 files changed

Lines changed: 197 additions & 3 deletions

File tree

crates/rbuilder-primitives/src/evm_inspector.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use ahash::HashMap;
1+
use ahash::{HashMap, HashSet};
22
use alloy_consensus::Transaction;
33
use alloy_primitives::{Address, B256, U256};
44
use alloy_rpc_types::AccessList;
@@ -34,6 +34,8 @@ pub struct UsedStateTrace {
3434
pub sent_amount: HashMap<Address, U256>,
3535
pub created_contracts: Vec<Address>,
3636
pub destructed_contracts: Vec<Address>,
37+
/// addresses whose code was read via EXTCODEHASH, EXTCODESIZE, or EXTCODECOPY
38+
pub read_code_addresses: HashSet<Address>,
3739
}
3840

3941
impl UsedStateTrace {
@@ -74,6 +76,8 @@ impl UsedStateTrace {
7476
}
7577
self.destructed_contracts.push(*address);
7678
}
79+
80+
self.read_code_addresses.extend(&other.read_code_addresses);
7781
}
7882

7983
pub fn clear(&mut self) {
@@ -84,6 +88,7 @@ impl UsedStateTrace {
8488
self.sent_amount.clear();
8589
self.created_contracts.clear();
8690
self.destructed_contracts.clear();
91+
self.read_code_addresses.clear();
8792
}
8893
}
8994

@@ -197,6 +202,12 @@ where
197202
let addr = interpreter.input.target_address;
198203
self.next_step_action = NextStepAction::ReadBalanceResult(addr);
199204
}
205+
opcode::EXTCODEHASH | opcode::EXTCODESIZE | opcode::EXTCODECOPY => {
206+
if let Ok(addr) = interpreter.stack.peek(0) {
207+
let addr = Address::from_word(B256::from(addr.to_be_bytes()));
208+
self.used_state_trace.read_code_addresses.insert(addr);
209+
}
210+
}
200211
_ => (),
201212
}
202213
}

crates/rbuilder/src/building/builders/parallel_builder/groups.rs

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ struct GroupData {
2929
writes: Vec<SlotKey>,
3030
balance_reads: Vec<Address>,
3131
balance_writes: Vec<Address>,
32+
code_reads: Vec<Address>,
3233
code_writes: Vec<Address>,
3334
conflicting_group_ids: HashSet<usize>,
3435
}
@@ -41,6 +42,7 @@ fn combine_groups(groups: Vec<GroupData>, removed_group_ids: Vec<usize>) -> Grou
4142
let mut writes = Vec::default();
4243
let mut balance_reads = Vec::default();
4344
let mut balance_writes = Vec::default();
45+
let mut code_reads = Vec::default();
4446
let mut code_writes = Vec::default();
4547
let mut conflicting_group_ids = removed_group_ids.into_iter().collect::<HashSet<usize>>();
4648
for group in groups {
@@ -49,6 +51,7 @@ fn combine_groups(groups: Vec<GroupData>, removed_group_ids: Vec<usize>) -> Grou
4951
writes.extend(group.writes);
5052
balance_reads.extend(group.balance_reads);
5153
balance_writes.extend(group.balance_writes);
54+
code_reads.extend(group.code_reads);
5255
code_writes.extend(group.code_writes);
5356
conflicting_group_ids.extend(group.conflicting_group_ids);
5457
}
@@ -60,6 +63,8 @@ fn combine_groups(groups: Vec<GroupData>, removed_group_ids: Vec<usize>) -> Grou
6063
balance_reads.dedup();
6164
balance_writes.sort_unstable();
6265
balance_writes.dedup();
66+
code_reads.sort_unstable();
67+
code_reads.dedup();
6368
code_writes.sort_unstable();
6469
code_writes.dedup();
6570

@@ -69,6 +74,7 @@ fn combine_groups(groups: Vec<GroupData>, removed_group_ids: Vec<usize>) -> Grou
6974
writes,
7075
balance_reads,
7176
balance_writes,
77+
code_reads,
7278
code_writes,
7379
conflicting_group_ids,
7480
}
@@ -82,6 +88,7 @@ pub struct ConflictFinder {
8288
group_writes: HashMap<Address, HashMap<B256, Vec<usize>>>, // same as above
8389
group_balance_reads: HashMap<Address, Vec<usize>>,
8490
group_balance_writes: HashMap<Address, Vec<usize>>,
91+
group_code_reads: HashMap<Address, Vec<usize>>,
8592
group_code_writes: HashMap<Address, Vec<usize>>,
8693
groups: HashMap<usize, GroupData>,
8794
orders: HashSet<OrderId>,
@@ -95,6 +102,7 @@ impl ConflictFinder {
95102
group_writes: HashMap::default(),
96103
group_balance_reads: HashMap::default(),
97104
group_balance_writes: HashMap::default(),
105+
group_code_reads: HashMap::default(),
98106
group_code_writes: HashMap::default(),
99107
groups: HashMap::default(),
100108
orders: HashSet::default(),
@@ -176,6 +184,16 @@ impl ConflictFinder {
176184
let inner_groups = inner_mapping.values().flatten();
177185
all_groups_in_conflict.extend(inner_groups);
178186
}
187+
// trying to create / destroy a contract other order is reading code from
188+
if let Some(group) = self.group_code_reads.get(contract_addr) {
189+
all_groups_in_conflict.extend_from_slice(group);
190+
}
191+
}
192+
// reading code of a contract other order is creating / destroying
193+
for code_read_addr in &used_state.read_code_addresses {
194+
if let Some(group) = self.group_code_writes.get(code_read_addr) {
195+
all_groups_in_conflict.extend_from_slice(group);
196+
}
179197
}
180198
all_groups_in_conflict.sort();
181199
all_groups_in_conflict.dedup();
@@ -198,12 +216,18 @@ impl ConflictFinder {
198216
code_writes.sort_unstable();
199217
code_writes.dedup();
200218

219+
let mut code_reads: Vec<Address> =
220+
used_state.read_code_addresses.into_iter().collect();
221+
code_reads.sort_unstable();
222+
code_reads.dedup();
223+
201224
GroupData {
202225
orders: vec![order],
203226
reads: used_state.read_slot_values.into_keys().collect(),
204227
writes: used_state.written_slot_values.into_keys().collect(),
205228
balance_reads: used_state.read_balances.into_keys().collect(),
206229
balance_writes,
230+
code_reads,
207231
code_writes,
208232
conflicting_group_ids: HashSet::default(),
209233
}
@@ -275,6 +299,12 @@ impl ConflictFinder {
275299
&group_data.balance_writes,
276300
&mut self.group_balance_writes,
277301
);
302+
add_group_to_map(
303+
group_id,
304+
is_new_id,
305+
&group_data.code_reads,
306+
&mut self.group_code_reads,
307+
);
278308
add_group_to_map(
279309
group_id,
280310
is_new_id,
@@ -302,6 +332,7 @@ impl ConflictFinder {
302332
&group_data.balance_writes,
303333
&mut self.group_balance_writes,
304334
);
335+
remove_group_from_map(group_id, &group_data.code_reads, &mut self.group_code_reads);
305336
remove_group_from_map(
306337
group_id,
307338
&group_data.code_writes,
@@ -440,6 +471,28 @@ mod tests {
440471
balance_write: Option<&Address>,
441472
contract_creation: Option<&Address>,
442473
contract_destruction: Option<&Address>,
474+
) -> Arc<SimulatedOrder> {
475+
self.create_order_with_code_read(
476+
read,
477+
write,
478+
balance_read,
479+
balance_write,
480+
contract_creation,
481+
contract_destruction,
482+
None,
483+
)
484+
}
485+
486+
#[allow(clippy::too_many_arguments)]
487+
pub fn create_order_with_code_read(
488+
&mut self,
489+
read: Option<&SlotKey>,
490+
write: Option<&SlotKey>,
491+
balance_read: Option<&Address>,
492+
balance_write: Option<&Address>,
493+
contract_creation: Option<&Address>,
494+
contract_destruction: Option<&Address>,
495+
code_read: Option<&Address>,
443496
) -> Arc<SimulatedOrder> {
444497
let mut trace = UsedStateTrace::default();
445498
if let Some(read) = read {
@@ -469,6 +522,9 @@ mod tests {
469522
if let Some(contract_address) = contract_destruction {
470523
trace.destructed_contracts.push(*contract_address);
471524
}
525+
if let Some(code_read_addr) = code_read {
526+
trace.read_code_addresses.insert(*code_read_addr);
527+
}
472528

473529
Arc::new(SimulatedOrder::new(
474530
Arc::new(Order::Tx(MempoolTx {
@@ -595,4 +651,72 @@ mod tests {
595651
let groups = cached_groups.get_order_groups();
596652
assert_eq!(groups.len(), 1);
597653
}
654+
655+
#[test]
656+
fn two_code_reads_no_conflict() {
657+
let mut data_gen = DataGenerator::new();
658+
let addr = Address::random();
659+
let oa =
660+
data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
661+
let ob =
662+
data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
663+
let mut cached_groups = ConflictFinder::new();
664+
cached_groups.add_orders(vec![oa, ob]);
665+
let groups = cached_groups.get_order_groups();
666+
assert_eq!(groups.len(), 2);
667+
}
668+
669+
#[test]
670+
fn code_read_and_creation_conflict() {
671+
let mut data_gen = DataGenerator::new();
672+
let addr = Address::random();
673+
let oa =
674+
data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
675+
let ob = data_gen.create_order(None, None, None, None, Some(&addr), None);
676+
let mut cached_groups = ConflictFinder::new();
677+
cached_groups.add_orders(vec![oa, ob]);
678+
let groups = cached_groups.get_order_groups();
679+
assert_eq!(groups.len(), 1);
680+
}
681+
682+
#[test]
683+
fn code_read_and_destruction_conflict() {
684+
let mut data_gen = DataGenerator::new();
685+
let addr = Address::random();
686+
let oa =
687+
data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
688+
let ob = data_gen.create_order(None, None, None, None, None, Some(&addr));
689+
let mut cached_groups = ConflictFinder::new();
690+
cached_groups.add_orders(vec![oa, ob]);
691+
let groups = cached_groups.get_order_groups();
692+
assert_eq!(groups.len(), 1);
693+
}
694+
695+
#[test]
696+
fn creation_then_code_read_conflict() {
697+
let mut data_gen = DataGenerator::new();
698+
let addr = Address::random();
699+
// code_write first, then code_read — tests the reverse direction
700+
let oa = data_gen.create_order(None, None, None, None, Some(&addr), None);
701+
let ob =
702+
data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
703+
let mut cached_groups = ConflictFinder::new();
704+
cached_groups.add_orders(vec![oa, ob]);
705+
let groups = cached_groups.get_order_groups();
706+
assert_eq!(groups.len(), 1);
707+
}
708+
709+
#[test]
710+
fn destruction_then_code_read_conflict() {
711+
let mut data_gen = DataGenerator::new();
712+
let addr = Address::random();
713+
// code_write first, then code_read — tests the reverse direction
714+
let oa = data_gen.create_order(None, None, None, None, None, Some(&addr));
715+
let ob =
716+
data_gen.create_order_with_code_read(None, None, None, None, None, None, Some(&addr));
717+
let mut cached_groups = ConflictFinder::new();
718+
cached_groups.add_orders(vec![oa, ob]);
719+
let groups = cached_groups.get_order_groups();
720+
assert_eq!(groups.len(), 1);
721+
}
598722
}

0 commit comments

Comments
 (0)