Skip to content

Commit c3d6db0

Browse files
authored
feat: add specific slots tracing (#588)
2 parents 5003a92 + 22d8121 commit c3d6db0

File tree

14 files changed

+337
-198
lines changed

14 files changed

+337
-198
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tycho-client/src/feed/component_tracker.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -273,7 +273,7 @@ where
273273
.entry(entrypoint.clone())
274274
.or_default()
275275
.contracts
276-
.extend(traces.called_addresses.iter().cloned());
276+
.extend(traces.accessed_slots.keys().cloned());
277277
}
278278

279279
// Update linked components for entrypoints

tycho-client/src/feed/synchronizer.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,10 @@ mod test {
779779
Bytes::from("0x0badc0ffee"),
780780
Bytes::from("0x0badc0ffee"),
781781
)]),
782-
called_addresses: HashSet::from([Bytes::from("0x0badc0ffee")]),
782+
accessed_slots: HashMap::from([(
783+
Bytes::from("0x0badc0ffee"),
784+
HashSet::from([Bytes::from("0xbadbeef0")]),
785+
)]),
783786
},
784787
)],
785788
)]),
@@ -841,7 +844,10 @@ mod test {
841844
Bytes::from("0x0badc0ffee"),
842845
Bytes::from("0x0badc0ffee"),
843846
)]),
844-
called_addresses: HashSet::from([Bytes::from("0x0badc0ffee")]),
847+
accessed_slots: HashMap::from([(
848+
Bytes::from("0x0badc0ffee"),
849+
HashSet::from([Bytes::from("0xbadbeef0")]),
850+
)]),
845851
},
846852
)],
847853
},
@@ -1014,7 +1020,10 @@ mod test {
10141020
Bytes::from("0x0badc0ffee"),
10151021
Bytes::from("0x0badc0ffee"),
10161022
)]),
1017-
called_addresses: HashSet::from([Bytes::from("0x0badc0ffee")]),
1023+
accessed_slots: HashMap::from([(
1024+
Bytes::from("0x0badc0ffee"),
1025+
HashSet::from([Bytes::from("0xbadbeef0")]),
1026+
)]),
10181027
},
10191028
)]),
10201029
},

tycho-client/src/rpc.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,9 +1151,11 @@ mod tests {
11511151
"0x0000000000000000000000000000000000000aaa"
11521152
]
11531153
],
1154-
"called_addresses": [
1155-
"0x0000000000000000000000000000000000aaaa"
1156-
]
1154+
"accessed_slots": {
1155+
"0x0000000000000000000000000000000000aaaa": [
1156+
"0x0000000000000000000000000000000000aaaa"
1157+
]
1158+
}
11571159
}
11581160
]
11591161
]
@@ -1210,10 +1212,13 @@ mod tests {
12101212
Bytes::from("0x0000000000000000000000000000000000000aaa")
12111213
)])
12121214
);
1213-
assert_eq!(trace_result.called_addresses.len(), 1);
1215+
assert_eq!(trace_result.accessed_slots.len(), 1);
12141216
assert_eq!(
1215-
trace_result.called_addresses,
1216-
HashSet::from([Bytes::from("0x0000000000000000000000000000000000aaaa")])
1217+
trace_result.accessed_slots,
1218+
HashMap::from([(
1219+
Bytes::from("0x0000000000000000000000000000000000aaaa"),
1220+
HashSet::from([Bytes::from("0x0000000000000000000000000000000000aaaa")])
1221+
)])
12171222
);
12181223
}
12191224
}

tycho-common/src/dto.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,7 @@ use utoipa::{IntoParams, ToSchema};
1818
use uuid::Uuid;
1919

2020
use crate::{
21-
models,
22-
models::ComponentId,
21+
models::{self, Address, ComponentId, StoreKey, StoreVal},
2322
serde_primitives::{
2423
hex_bytes, hex_bytes_option, hex_hashmap_key, hex_hashmap_key_value, hex_hashmap_value,
2524
},
@@ -1424,14 +1423,14 @@ impl From<models::blockchain::EntryPointWithTracingParams> for EntryPointWithTra
14241423
#[derive(Serialize, Deserialize, Debug, Default, PartialEq, ToSchema, Eq, Clone)]
14251424
pub struct TracingResult {
14261425
#[schema(value_type=HashSet<(String, String)>)]
1427-
pub retriggers: HashSet<(Bytes, Bytes)>,
1428-
#[schema(value_type=HashSet<String>)]
1429-
pub called_addresses: HashSet<Bytes>,
1426+
pub retriggers: HashSet<(StoreKey, StoreVal)>,
1427+
#[schema(value_type=HashMap<String,HashSet<String>>)]
1428+
pub accessed_slots: HashMap<Address, HashSet<StoreKey>>,
14301429
}
14311430

14321431
impl From<models::blockchain::TracingResult> for TracingResult {
14331432
fn from(value: models::blockchain::TracingResult) -> Self {
1434-
TracingResult { retriggers: value.retriggers, called_addresses: value.called_addresses }
1433+
TracingResult { retriggers: value.retriggers, accessed_slots: value.accessed_slots }
14351434
}
14361435
}
14371436

@@ -1476,9 +1475,13 @@ impl From<TracedEntryPointRequestResponse> for DCIUpdate {
14761475
existing_trace
14771476
.retriggers
14781477
.extend(trace.retriggers.clone());
1479-
existing_trace
1480-
.called_addresses
1481-
.extend(trace.called_addresses.clone());
1478+
for (address, slots) in trace.accessed_slots.clone() {
1479+
existing_trace
1480+
.accessed_slots
1481+
.entry(address)
1482+
.or_default()
1483+
.extend(slots);
1484+
}
14821485
})
14831486
.or_insert(trace);
14841487
}
@@ -2006,7 +2009,9 @@ mod test {
20062009
"retriggers": [
20072010
["0x01", "0x02"]
20082011
],
2009-
"called_addresses": ["0x03", "0x04"]
2012+
"accessed_slots": {
2013+
"0x03": ["0x03", "0x04"]
2014+
}
20102015
}
20112016
}
20122017
}
@@ -2121,7 +2126,9 @@ mod test {
21212126
"retriggers": [
21222127
["0x01", "0x02"]
21232128
],
2124-
"called_addresses": ["0x03", "0x04"]
2129+
"accessed_slots": {
2130+
"0x03": ["0x03", "0x04"]
2131+
}
21252132
}
21262133
}
21272134
}

tycho-common/src/models/blockchain.rs

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -483,25 +483,30 @@ pub struct TracingResult {
483483
/// A set of (address, storage slot) pairs representing state that contain a called address.
484484
/// If any of these storage slots change, the execution path might change.
485485
pub retriggers: HashSet<(Address, StoreKey)>,
486-
/// A set of all addresses that were called during the trace.
487-
pub called_addresses: HashSet<Address>,
486+
/// A map of all addresses that were called during the trace with a list of storage slots that
487+
/// were accessed.
488+
pub accessed_slots: HashMap<Address, HashSet<StoreKey>>,
488489
}
489490

490491
impl TracingResult {
491492
pub fn new(
492493
retriggers: HashSet<(Address, StoreKey)>,
493-
called_addresses: HashSet<Address>,
494+
accessed_slots: HashMap<Address, HashSet<StoreKey>>,
494495
) -> Self {
495-
Self { retriggers, called_addresses }
496+
Self { retriggers, accessed_slots }
496497
}
497498

498499
/// Merges this tracing result with another one.
499500
///
500501
/// The method combines two [`TracingResult`] instances.
501502
pub fn merge(&mut self, other: TracingResult) {
502503
self.retriggers.extend(other.retriggers);
503-
self.called_addresses
504-
.extend(other.called_addresses);
504+
for (address, slots) in other.accessed_slots {
505+
self.accessed_slots
506+
.entry(address)
507+
.or_default()
508+
.extend(slots);
509+
}
505510
}
506511
}
507512

@@ -806,12 +811,18 @@ pub mod fixtures {
806811

807812
let mut result1 = TracingResult::new(
808813
HashSet::from([(address1.clone(), store_key1.clone())]),
809-
HashSet::from([address2.clone(), address3.clone()]),
814+
HashMap::from([
815+
(address2.clone(), HashSet::from([store_key1.clone()])),
816+
(address3.clone(), HashSet::from([store_key2.clone()])),
817+
]),
810818
);
811819

812820
let result2 = TracingResult::new(
813821
HashSet::from([(address3.clone(), store_key2.clone())]),
814-
HashSet::from([address1.clone()]),
822+
HashMap::from([
823+
(address1.clone(), HashSet::from([store_key1.clone()])),
824+
(address2.clone(), HashSet::from([store_key2.clone()])),
825+
]),
815826
);
816827

817828
result1.merge(result2);
@@ -820,21 +831,29 @@ pub mod fixtures {
820831
assert_eq!(result1.retriggers.len(), 2);
821832
assert!(result1
822833
.retriggers
823-
.contains(&(address1.clone(), store_key1)));
834+
.contains(&(address1.clone(), store_key1.clone())));
824835
assert!(result1
825836
.retriggers
826837
.contains(&(address3.clone(), store_key2.clone())));
827838

828-
// Verify called_addresses were merged
829-
assert_eq!(result1.called_addresses.len(), 3);
839+
// Verify accessed slots were merged
840+
assert_eq!(result1.accessed_slots.len(), 3);
830841
assert!(result1
831-
.called_addresses
832-
.contains(&address1));
842+
.accessed_slots
843+
.contains_key(&address1));
833844
assert!(result1
834-
.called_addresses
835-
.contains(&address2));
845+
.accessed_slots
846+
.contains_key(&address2));
836847
assert!(result1
837-
.called_addresses
838-
.contains(&address3));
848+
.accessed_slots
849+
.contains_key(&address3));
850+
851+
assert_eq!(
852+
result1
853+
.accessed_slots
854+
.get(&address2)
855+
.unwrap(),
856+
&HashSet::from([store_key1.clone(), store_key2.clone()])
857+
);
839858
}
840859
}

tycho-common/src/traits.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ pub trait EntryPointTracer: Sync {
138138
/// * `retriggers` - A set of (address, storage slot) pairs representing storage locations that
139139
/// could alter tracing results. If any of these storage slots change, the set of called
140140
/// contract might be outdated.
141-
/// * `called_addresses` - A set of all contract addresses that were called during the trace
141+
/// * `accessed_slots` - A map of all contract addresses that were called during the trace with
142+
/// a list of storage slots that were accessed (read or written).
142143
async fn trace(
143144
&self,
144145
block_hash: BlockHash,

tycho-ethereum/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ clap = { version = "4", features = ["derive", "env"], optional = true }
4141
humantime = { version = "2.1.0", optional = true }
4242
tracing-test = "0.2.5"
4343

44+
[dev-dependencies]
45+
pretty_assertions.workspace = true
46+
4447
[features]
4548
default = []
4649
onchain_data = ["ethrpc", "ethcontract", "dep:contracts", "humantime", "clap"]

0 commit comments

Comments
 (0)