Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion crates/rbuilder-primitives/src/evm_inspector.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -34,6 +34,8 @@ pub struct UsedStateTrace {
pub sent_amount: HashMap<Address, U256>,
pub created_contracts: Vec<Address>,
pub destructed_contracts: Vec<Address>,
/// addresses whose code was read via EXTCODEHASH, EXTCODESIZE, or EXTCODECOPY
pub read_code_addresses: HashSet<Address>,
}

impl UsedStateTrace {
Expand Down Expand Up @@ -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) {
Expand All @@ -84,6 +88,7 @@ impl UsedStateTrace {
self.sent_amount.clear();
self.created_contracts.clear();
self.destructed_contracts.clear();
self.read_code_addresses.clear();
}
}

Expand Down Expand Up @@ -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);
}
}
_ => (),
}
}
Expand Down
123 changes: 123 additions & 0 deletions crates/rbuilder/src/building/builders/parallel_builder/groups.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ struct GroupData {
writes: Vec<SlotKey>,
balance_reads: Vec<Address>,
balance_writes: Vec<Address>,
code_reads: Vec<Address>,
code_writes: Vec<Address>,
conflicting_group_ids: HashSet<usize>,
}
Expand All @@ -41,6 +42,7 @@ fn combine_groups(groups: Vec<GroupData>, removed_group_ids: Vec<usize>) -> 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::<HashSet<usize>>();
for group in groups {
Expand All @@ -49,6 +51,7 @@ fn combine_groups(groups: Vec<GroupData>, removed_group_ids: Vec<usize>) -> 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);
}
Expand All @@ -60,6 +63,8 @@ fn combine_groups(groups: Vec<GroupData>, removed_group_ids: Vec<usize>) -> 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();

Expand All @@ -69,6 +74,7 @@ fn combine_groups(groups: Vec<GroupData>, removed_group_ids: Vec<usize>) -> Grou
writes,
balance_reads,
balance_writes,
code_reads,
code_writes,
conflicting_group_ids,
}
Expand All @@ -82,6 +88,7 @@ pub struct ConflictFinder {
group_writes: HashMap<Address, HashMap<B256, Vec<usize>>>, // same as above
group_balance_reads: HashMap<Address, Vec<usize>>,
group_balance_writes: HashMap<Address, Vec<usize>>,
group_code_reads: HashMap<Address, Vec<usize>>,
group_code_writes: HashMap<Address, Vec<usize>>,
groups: HashMap<usize, GroupData>,
orders: HashSet<OrderId>,
Expand All @@ -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(),
Expand Down Expand Up @@ -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();
Expand All @@ -198,12 +216,18 @@ impl ConflictFinder {
code_writes.sort_unstable();
code_writes.dedup();

let mut code_reads: Vec<Address> =
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(),
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -440,6 +471,27 @@ mod tests {
balance_write: Option<&Address>,
contract_creation: Option<&Address>,
contract_destruction: Option<&Address>,
) -> Arc<SimulatedOrder> {
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<SimulatedOrder> {
let mut trace = UsedStateTrace::default();
if let Some(read) = read {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
}
Loading