Skip to content

Commit 9614b9c

Browse files
authored
feat: Postgres and Gateway functions for EntryPoints (#551)
2 parents 423f90a + c522c50 commit 9614b9c

File tree

16 files changed

+1850
-172
lines changed

16 files changed

+1850
-172
lines changed

tycho-common/src/models/blockchain.rs

Lines changed: 129 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
use std::{
22
any::Any,
3-
collections::{hash_map::Entry, HashMap},
3+
collections::{hash_map::Entry, HashMap, HashSet},
44
sync::Arc,
55
};
66

77
use chrono::NaiveDateTime;
8-
use serde::{Deserialize, Serialize};
8+
use serde::{ser::SerializeStruct, Deserialize, Serialize, Serializer};
99
use tracing::warn;
1010

11+
use super::{BlockHash, StoreKey};
1112
use crate::{
1213
models::{
1314
contract::{AccountBalance, AccountChangesWithTx, AccountDelta},
@@ -341,6 +342,111 @@ pub enum BlockTag {
341342
/// Block by number
342343
Number(u64),
343344
}
345+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
346+
pub struct EntryPoint {
347+
/// The id of the protocol component that the entry point belongs to.
348+
pub external_id: String,
349+
/// The address of the contract to trace.
350+
pub target: Address,
351+
/// The signature of the function to trace.
352+
pub signature: String,
353+
}
354+
355+
impl EntryPoint {
356+
pub fn new(external_id: String, target: Address, signature: String) -> Self {
357+
Self { external_id, target, signature }
358+
}
359+
}
360+
361+
/// A struct that combines an entry point with its associated tracing data.
362+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
363+
pub struct EntryPointWithData {
364+
/// The entry point to trace, containing the target contract address and function signature
365+
pub entry_point: EntryPoint,
366+
/// The tracing configuration and data for this entry point
367+
pub data: EntryPointTracingData,
368+
}
369+
370+
impl EntryPointWithData {
371+
pub fn new(entry_point: EntryPoint, data: EntryPointTracingData) -> Self {
372+
Self { entry_point, data }
373+
}
374+
}
375+
376+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Eq, Hash)]
377+
/// An entry point to trace. Different types of entry points tracing will be supported in the
378+
/// future. Like RPC debug tracing, symbolic execution, etc.
379+
pub enum EntryPointTracingData {
380+
/// Uses RPC calls to retrieve the called addresses and retriggers
381+
RPCTracer(RPCTracerEntryPoint),
382+
}
383+
384+
#[derive(Debug, Clone, PartialEq, Deserialize, Eq, Hash)]
385+
pub struct RPCTracerEntryPoint {
386+
/// The caller address of the transaction, if not provided tracing will use the default value
387+
/// for an address defined by the VM.
388+
pub caller: Option<Address>,
389+
/// The data used for the tracing call, this needs to include the function selector
390+
pub data: Bytes,
391+
}
392+
393+
impl RPCTracerEntryPoint {
394+
pub fn new(caller: Option<Address>, data: Bytes) -> Self {
395+
Self { caller, data }
396+
}
397+
}
398+
399+
// Ensure serialization order, required by the storage layer
400+
impl Serialize for RPCTracerEntryPoint {
401+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
402+
where
403+
S: Serializer,
404+
{
405+
let mut state = serializer.serialize_struct("RPCTracerEntryPoint", 2)?;
406+
state.serialize_field("caller", &self.caller)?;
407+
state.serialize_field("data", &self.data)?;
408+
state.end()
409+
}
410+
}
411+
412+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
413+
pub struct TracingResult {
414+
/// A set of (address, storage slot) pairs representing state that contain a called address.
415+
/// If any of these storage slots change, the execution path might change.
416+
pub retriggers: HashSet<(Address, StoreKey)>,
417+
/// A set of all addresses that were called during the trace.
418+
pub called_addresses: HashSet<Address>,
419+
}
420+
421+
impl TracingResult {
422+
pub fn new(
423+
retriggers: HashSet<(Address, StoreKey)>,
424+
called_addresses: HashSet<Address>,
425+
) -> Self {
426+
Self { retriggers, called_addresses }
427+
}
428+
}
429+
430+
#[derive(Debug, Clone, PartialEq)]
431+
/// Represents a traced entry point and the results of the tracing operation.
432+
pub struct TracedEntryPoint {
433+
/// The combined entry point and tracing data that was traced
434+
pub entry_point_with_data: EntryPointWithData,
435+
/// The block hash of the block that the entry point was traced on.
436+
pub detection_block_hash: BlockHash,
437+
/// The results of the tracing operation
438+
pub tracing_result: TracingResult,
439+
}
440+
441+
impl TracedEntryPoint {
442+
pub fn new(
443+
entry_point: EntryPointWithData,
444+
detection_block_hash: BlockHash,
445+
result: TracingResult,
446+
) -> Self {
447+
Self { entry_point_with_data: entry_point, detection_block_hash, tracing_result: result }
448+
}
449+
}
344450

345451
#[cfg(test)]
346452
pub mod fixtures {
@@ -585,4 +691,25 @@ pub mod fixtures {
585691

586692
assert!(tx1.merge(tx2).is_err());
587693
}
694+
695+
#[test]
696+
fn test_rpc_tracer_entry_point_serialization_order() {
697+
use std::str::FromStr;
698+
699+
use serde_json;
700+
701+
let entry_point = RPCTracerEntryPoint::new(
702+
Some(Address::from_str("0x1234567890123456789012345678901234567890").unwrap()),
703+
Bytes::from_str("0xabcdef").unwrap(),
704+
);
705+
706+
let serialized = serde_json::to_string(&entry_point).unwrap();
707+
708+
// Verify that "caller" comes before "data" in the serialized output
709+
assert!(serialized.find("\"caller\"").unwrap() < serialized.find("\"data\"").unwrap());
710+
711+
// Verify we can deserialize it back
712+
let deserialized: RPCTracerEntryPoint = serde_json::from_str(&serialized).unwrap();
713+
assert_eq!(entry_point, deserialized);
714+
}
588715
}

tycho-common/src/models/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ pub type AccountToContractStore = HashMap<Address, ContractStore>;
5151
/// Component id literal type to uniquely identify a component.
5252
pub type ComponentId = String;
5353

54+
/// Protocol system literal type to uniquely identify a protocol system.
55+
pub type ProtocolSystem = String;
56+
57+
/// Entry point id literal type to uniquely identify an entry point.
58+
pub type EntryPointId = String;
59+
5460
#[derive(
5561
Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, EnumString, Display, Default,
5662
)]

tycho-common/src/storage.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
//! Storage traits used by Tycho
2-
use std::{collections::HashMap, fmt::Display};
2+
use std::{
3+
collections::{HashMap, HashSet},
4+
fmt::Display,
5+
};
36

47
use async_trait::async_trait;
58
use chrono::NaiveDateTime;
@@ -8,15 +11,15 @@ use thiserror::Error;
811
use crate::{
912
dto,
1013
models::{
11-
blockchain::{Block, Transaction},
14+
blockchain::{Block, EntryPointWithData, TracedEntryPoint, TracingResult, Transaction},
1215
contract::{Account, AccountBalance, AccountDelta},
1316
protocol::{
1417
ComponentBalance, ProtocolComponent, ProtocolComponentState,
1518
ProtocolComponentStateDelta, QualityRange,
1619
},
1720
token::CurrencyToken,
18-
Address, BlockHash, Chain, ComponentId, ContractId, ExtractionState, PaginationParams,
19-
ProtocolType, TxHash,
21+
Address, BlockHash, Chain, ComponentId, ContractId, EntryPointId, ExtractionState,
22+
PaginationParams, ProtocolSystem, ProtocolType, TxHash,
2023
},
2124
Bytes,
2225
};
@@ -50,7 +53,7 @@ impl Display for BlockIdentifier {
5053
}
5154
}
5255

53-
#[derive(Error, Debug, PartialEq)]
56+
#[derive(Error, Debug, PartialEq, Clone)]
5457
pub enum StorageError {
5558
#[error("Could not find {0} with id `{1}`!")]
5659
NotFound(String, String),
@@ -484,6 +487,46 @@ pub trait ProtocolGateway {
484487
) -> Result<WithTotal<Vec<String>>, StorageError>;
485488
}
486489

490+
/// Filters for entry points queries in the database.
491+
// Shalow but can be used to add more filters without breaking backwards compatibility in the future
492+
pub struct EntryPointFilter {
493+
pub protocol_system: ProtocolSystem,
494+
}
495+
496+
impl EntryPointFilter {
497+
pub fn new(protocol: ProtocolSystem) -> Self {
498+
Self { protocol_system: protocol }
499+
}
500+
}
501+
502+
// Trait for entry point gateway operations.
503+
#[async_trait]
504+
pub trait EntryPointGateway {
505+
/// Upserts a list of entry points with their tracing data into the database.
506+
async fn upsert_entry_points_with_data(
507+
&self,
508+
entry_points: &[(ComponentId, Vec<EntryPointWithData>)],
509+
) -> Result<(), StorageError>;
510+
511+
/// Retrieves a list of entry points with their tracing data from the database.
512+
async fn get_entry_points_with_data(
513+
&self,
514+
filter: EntryPointFilter,
515+
) -> Result<Vec<EntryPointWithData>, StorageError>;
516+
517+
/// Upserts a list of traced entry points into the database.
518+
async fn upsert_traced_entry_points(
519+
&self,
520+
traced_entry_points: &[TracedEntryPoint],
521+
) -> Result<(), StorageError>;
522+
523+
/// Retrieves all tracing results for a set of entry points from the database.
524+
async fn get_traced_entry_points(
525+
&self,
526+
entry_points: &HashSet<EntryPointId>,
527+
) -> Result<HashMap<EntryPointId, Vec<TracingResult>>, StorageError>;
528+
}
529+
487530
/// Manage contracts and their state in storage.
488531
///
489532
/// Specifies how to retrieve, add and update contracts in storage.

tycho-common/src/traits.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ use async_trait::async_trait;
55

66
use crate::{
77
models::{
8-
blockchain::{Block, BlockTag},
8+
blockchain::{Block, BlockTag, EntryPointWithData, TracedEntryPoint},
99
contract::AccountDelta,
1010
token::{CurrencyToken, TokenQuality, TransferCost, TransferTax},
11-
Address, Balance,
11+
Address, Balance, BlockHash,
1212
},
1313
Bytes,
1414
};
@@ -93,3 +93,28 @@ pub trait TokenPreProcessor: Send + Sync {
9393
block: BlockTag,
9494
) -> Vec<CurrencyToken>;
9595
}
96+
97+
/// Trait for tracing blockchain transaction execution.
98+
#[async_trait]
99+
pub trait EntryPointTracer {
100+
type Error;
101+
102+
/// Traces the execution of a list of entry points at a specific block.
103+
///
104+
/// # Parameters
105+
/// * `block_hash` - The hash of the block at which to perform the trace. The trace will use the
106+
/// state of the blockchain at this block.
107+
/// * `entry_points` - A list of entry points to trace with their data.
108+
///
109+
/// # Returns
110+
/// Returns a vector of `TracedEntryPoint`, where each element contains:
111+
/// * `retriggers` - A set of (address, storage slot) pairs representing storage locations that
112+
/// could alter tracing results. If any of these storage slots change, the set of called
113+
/// contract might be outdated.
114+
/// * `called_addresses` - A set of all contract addresses that were called during the trace
115+
async fn trace(
116+
&self,
117+
block_hash: BlockHash,
118+
entry_points: Vec<EntryPointWithData>,
119+
) -> Result<Vec<TracedEntryPoint>, Self::Error>;
120+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod tracer;

0 commit comments

Comments
 (0)