@@ -5,11 +5,21 @@ use eyre::Result;
5
5
use std:: sync:: Arc ;
6
6
use tokio:: { select, sync:: mpsc:: UnboundedReceiver , task:: JoinSet } ;
7
7
use trevm:: {
8
- db:: { cow:: CacheOnWrite , sync:: { ConcurrentState , ConcurrentStateInfo } } , helpers:: Ctx , revm:: {
8
+ Cfg , DbConnect , NoopBlock , TrevmBuilder , TrevmBuilderError , Tx ,
9
+ db:: {
10
+ cow:: CacheOnWrite ,
11
+ sync:: { ConcurrentState , ConcurrentStateInfo } ,
12
+ } ,
13
+ helpers:: Ctx ,
14
+ revm:: {
15
+ Database , DatabaseCommit , DatabaseRef , Inspector ,
9
16
context:: {
10
- result:: { EVMError , ExecutionResult , ResultAndState } , CfgEnv
11
- } , primitives:: address, state:: Account , Database , DatabaseCommit , DatabaseRef , Inspector
12
- } , BlockDriver , Cfg , DbConnect , EvmFactory , NoopBlock , TrevmBuilder , TrevmBuilderError , Tx
17
+ CfgEnv ,
18
+ result:: { EVMError , ExecutionResult , ResultAndState } ,
19
+ } ,
20
+ primitives:: address,
21
+ state:: Account ,
22
+ } ,
13
23
} ;
14
24
15
25
/// Tracks the EVM state, score, and result of an EVM execution.
@@ -28,29 +38,33 @@ pub struct Best<T, S: PartialOrd + Ord = U256> {
28
38
/// Binds a database and an inspector together for simulation.
29
39
#[ derive( Debug , Clone ) ]
30
40
pub struct SimulatorFactory < Db , Insp > {
31
- /// The database state the execution is carried out on.
32
- pub db : Db ,
33
41
/// The inspector
34
42
pub inspector : Insp ,
43
+ /// A CacheOnWrite that is cloneable
44
+ pub cow : MakeCow < Db > ,
35
45
}
36
46
37
47
/// SimResult is an [`Option`] type that holds a tuple of a transaction and its associated
38
48
/// state as a [`Db`] type updates if it was successfully executed.
39
- type SimResult < Db > = Result < Option < ( Best < TxEnvelope > , ConcurrentState < Arc < ConcurrentState < Db > > > ) > > ;
49
+ type SimResult < Db > = Result < Option < ( Best < TxEnvelope > , CacheOnWrite < Arc < ConcurrentState < Db > > > ) > > ;
40
50
41
51
impl < Db , Insp > SimulatorFactory < Db , Insp >
42
52
where
43
- Insp : Inspector < Ctx < ConcurrentState < Db > > >
44
- + Inspector < Ctx < ConcurrentState < Arc < ConcurrentState < Db > > > > >
53
+ Insp : Inspector < Ctx < CacheOnWrite < CacheOnWrite < Arc < ConcurrentState < Db > > > > > >
45
54
+ Send
46
55
+ Sync
47
56
+ Clone
48
57
+ ' static ,
49
58
Db : Database + DatabaseRef + DatabaseCommit + Send + Sync + Clone + ' static ,
59
+ MakeCow < Db > : DbConnect < Database = CacheOnWrite < Arc < ConcurrentState < Db > > > > ,
50
60
{
51
61
/// Creates a new Simulator factory from the provided database and inspector.
52
- pub const fn new ( db : Db , inspector : Insp ) -> Self {
53
- Self { db, inspector }
62
+ pub fn new ( db : Db , inspector : Insp ) -> Self {
63
+ let cdb = ConcurrentState :: new ( db, ConcurrentStateInfo :: default ( ) ) ;
64
+ let cdb = Arc :: new ( cdb) ;
65
+ let cow = MakeCow :: new ( cdb) ;
66
+
67
+ Self { inspector, cow }
54
68
}
55
69
56
70
/// Spawns a trevm simulator that runs until `deadline` is hit.
@@ -68,60 +82,62 @@ where
68
82
{
69
83
tokio:: spawn ( async move {
70
84
let mut join_set = JoinSet :: new ( ) ;
71
- let mut best: Option < Best < TxEnvelope > > = None ;
72
85
let mut block = InProgressBlock :: new ( ) ;
73
86
74
87
let sleep = tokio:: time:: sleep_until ( deadline) ;
75
88
tokio:: pin!( sleep) ;
76
89
77
90
loop {
78
91
select ! {
79
- _ = & mut sleep => break ,
80
- // Handle incoming
92
+ _ = & mut sleep => {
93
+ tracing:: debug!( "deadline reached, stopping simulation" ) ;
94
+ break ;
95
+ } ,
81
96
tx = inbound_tx. recv( ) => {
97
+ tracing:: debug!( "#### received transaction" ) ;
82
98
if let Some ( inbound_tx) = tx {
83
- // Setup the simulation environment
84
- let sim = self . clone( ) ;
85
99
let eval = evaluator. clone( ) ;
86
- let db = self . connect ( ) . expect ( "must connect db" ) ;
87
- let mut parent_db = Arc :: new ( db ) ;
100
+ let sim = self . clone ( ) ;
101
+ let db = self . cow . connect ( ) . unwrap ( ) ;
88
102
89
103
join_set. spawn( async move {
90
- let result = sim. simulate_tx( inbound_tx, eval, parent_db. child( ) ) ;
91
-
92
- if let Ok ( Some ( ( best, db) ) ) = result {
93
- if let Ok ( ( ) ) = parent_db. merge_child( db) {
94
- tracing:: debug!( "merging updated simulation state" ) ;
95
- return Some ( best)
104
+ let result = sim. simulate_tx( inbound_tx, eval, db. nest( ) ) ;
105
+ match result {
106
+ Ok ( Some ( ( best, new_db) ) ) => {
107
+ tracing:: debug!( "simulation completed, attempting to update state" ) ;
108
+ // TODO: call cow.flatten on the nest instead
109
+ tracing:: debug!( "successfully merged simulation state" ) ;
110
+ return Some ( best) ;
111
+ }
112
+ Ok ( None ) => {
113
+ tracing:: debug!( "simulation returned no result" ) ;
114
+ return None ;
115
+ }
116
+ Err ( e) => {
117
+ tracing:: error!( e = ?e, "failed to simulate transaction" ) ;
118
+ return None ;
96
119
}
97
- tracing:: error!( "failed to update simulation state" ) ;
98
- None
99
- } else {
100
- None
101
120
}
102
121
} ) ;
103
122
}
104
123
}
105
124
Some ( result) = join_set. join_next( ) => {
125
+ println!( "join_set result" ) ;
106
126
match result {
107
- Ok ( Some ( candidate) ) => {
108
- tracing:: info!( tx_hash = ?candidate. tx. tx_hash( ) , "ingesting transaction" ) ;
109
- block. ingest_tx( candidate. tx. as_ref( ) ) ;
110
-
111
- if candidate. score > best. as_ref( ) . map( |b| b. score) . unwrap_or_default( ) {
112
- tracing:: info!( score = ?candidate. score, "new best candidate found" ) ;
113
- best = Some ( candidate) ;
114
- }
115
- }
127
+ Ok ( Some ( best) ) => {
128
+ println!( "simulation completed" ) ;
129
+ block. ingest_tx( best. tx. as_ref( ) ) ;
130
+ } ,
116
131
Ok ( None ) => {
132
+ println!( "simulation returned no result" ) ;
117
133
tracing:: debug!( "simulation returned no result" ) ;
118
134
}
119
135
Err ( e) => {
120
- tracing:: error!( "simulation task failed: {}" , e) ;
136
+ println!( "simulation returned an error: {}" , e) ;
137
+ tracing:: error!( e = ?e, "failed to simulate transaction" ) ;
121
138
}
122
139
}
123
140
}
124
- else => break ,
125
141
}
126
142
}
127
143
@@ -131,10 +147,10 @@ where
131
147
132
148
/// Simulates an inbound tx and applies its state if it's successfully simualted
133
149
pub fn simulate_tx < F > (
134
- self ,
150
+ & self ,
135
151
tx : TxEnvelope ,
136
152
evaluator : Arc < F > ,
137
- db : ConcurrentState < Arc < ConcurrentState < Db > > > ,
153
+ db : CacheOnWrite < CacheOnWrite < Arc < ConcurrentState < Db > > > > ,
138
154
) -> SimResult < Db >
139
155
where
140
156
F : Fn ( & ResultAndState ) -> U256 + Send + Sync + ' static ,
@@ -151,6 +167,9 @@ where
151
167
let score = evaluator ( & result) ;
152
168
let best = Best { tx : Arc :: new ( tx) , result, score } ;
153
169
170
+ // Flatten to save the result to the parent and return it
171
+ let db = db. flatten ( ) ;
172
+
154
173
Ok ( Some ( ( best, db) ) )
155
174
}
156
175
Err ( terr) => {
@@ -175,106 +194,31 @@ where
175
194
}
176
195
}
177
196
178
- impl < Db , Insp > DbConnect for SimulatorFactory < Db , Insp >
179
- where
180
- Db : Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + ' static ,
181
- Insp : Inspector < Ctx < ConcurrentState < Db > > > + Sync + Send + Clone ,
182
- {
183
- type Database = ConcurrentState < Db > ;
184
- type Error = TrevmBuilderError ;
185
-
186
- fn connect ( & self ) -> Result < Self :: Database , Self :: Error > {
187
- let inner = ConcurrentState :: new ( self . db . clone ( ) , ConcurrentStateInfo :: default ( ) ) ;
188
- Ok ( inner)
189
- }
190
- }
197
+ /// MakeCow wraps a ConcurrentState database in an Arc to allow for cloning.
198
+ #[ derive( Debug , Clone ) ]
199
+ pub struct MakeCow < Db > ( Arc < ConcurrentState < Db > > ) ;
191
200
192
- /// Makes a SimulatorFactory capable of creating and configuring trevm instances
193
- impl < Db , Insp > EvmFactory for SimulatorFactory < Db , Insp >
201
+ impl < Db > MakeCow < Db >
194
202
where
195
- Db : Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + ' static ,
196
- Insp : Inspector < Ctx < ConcurrentState < Db > > > + Sync + Send + Clone ,
203
+ Db : Database + DatabaseRef + DatabaseCommit + Send + Sync + ' static ,
197
204
{
198
- type Insp = Insp ;
199
-
200
- fn create (
201
- & self ,
202
- ) -> std:: result:: Result < trevm:: EvmNeedsCfg < Self :: Database , Self :: Insp > , Self :: Error > {
203
- let db = self . connect ( ) ?;
204
- let result =
205
- TrevmBuilder :: new ( ) . with_db ( db) . with_insp ( self . inspector . clone ( ) ) . build_trevm ( ) ;
206
- match result {
207
- Ok ( t) => Ok ( t) ,
208
- Err ( e) => Err ( e. into ( ) ) ,
209
- }
205
+ /// Returns a new CoW Db that implements Clone for use in DbConnect
206
+ pub fn new ( db : Arc < ConcurrentState < Db > > ) -> Self {
207
+ Self ( db)
210
208
}
211
209
}
212
210
213
- pub trait BlockExtractor < Insp , Db >
211
+ impl < Db > DbConnect for MakeCow < Db >
214
212
where
215
- Db : Database + DatabaseCommit ,
216
- Insp : Inspector < Ctx < Db > > ,
213
+ Db : Database + DatabaseRef + DatabaseCommit + Sync + Send + Clone + ' static ,
217
214
{
218
- /// BlockDriver runs the transactions over the provided trevm instance.
219
- type Driver : BlockDriver < Insp , Error < Db > : core:: error:: Error > ;
220
-
221
- /// Instantiate an configure a new [`trevm`] instance.
222
- fn trevm ( & self , db : Db ) -> trevm:: EvmNeedsBlock < Db , Insp > ;
223
-
224
- /// Extracts transactions from the source.
225
- ///
226
- /// Extraction is infallible. Worst case it should return a no-op driver.
227
- fn extract ( & mut self , bytes : & [ u8 ] ) -> Self :: Driver ;
228
- }
229
-
230
- impl < Insp > BlockDriver < Insp > for InProgressBlock {
231
- type Block = NoopBlock ;
232
-
233
- type Error < Db : Database + DatabaseCommit > = Error < Db > ;
234
-
235
- fn block ( & self ) -> & Self :: Block {
236
- & NoopBlock
237
- }
238
-
239
- /// Loops through the transactions in the block and runs them, accepting the state at the end
240
- /// if it was successful and returning and erroring out otherwise.
241
- fn run_txns < Db : Database + DatabaseCommit > (
242
- & mut self ,
243
- mut trevm : trevm:: EvmNeedsTx < Db , Insp > ,
244
- ) -> trevm:: RunTxResult < Db , Insp , Self >
245
- where
246
- Insp : Inspector < Ctx < Db > > ,
247
- {
248
- for tx in self . transactions ( ) . iter ( ) {
249
- if tx. recover_signer ( ) . is_ok ( ) {
250
- let sender = tx. recover_signer ( ) . unwrap ( ) ;
251
- tracing:: info!( sender = ?sender, tx_hash = ?tx. tx_hash( ) , "simulating transaction" ) ;
252
-
253
- let t = match trevm. run_tx ( tx) {
254
- Ok ( t) => t,
255
- Err ( e) => {
256
- if e. is_transaction_error ( ) {
257
- return Ok ( e. discard_error ( ) ) ;
258
- } else {
259
- return Err ( e. err_into ( ) ) ;
260
- }
261
- }
262
- } ;
263
-
264
- ( _, trevm) = t. accept ( ) ;
265
- }
266
- }
267
- Ok ( trevm)
268
- }
215
+ type Database = CacheOnWrite < Arc < ConcurrentState < Db > > > ;
216
+ type Error = TrevmBuilderError ;
269
217
270
- fn post_block < Db : Database + DatabaseCommit > (
271
- & mut self ,
272
- _trevm : & trevm:: EvmNeedsBlock < Db , Insp > ,
273
- ) -> Result < ( ) , Self :: Error < Db > >
274
- where
275
- Insp : Inspector < Ctx < Db > > ,
276
- {
277
- Ok ( ( ) )
218
+ /// Connects to the database and returns a CacheOnWrite instance
219
+ fn connect ( & self ) -> Result < Self :: Database , Self :: Error > {
220
+ let db: CacheOnWrite < Arc < ConcurrentState < Db > > > = CacheOnWrite :: new ( self . 0 . clone ( ) ) ;
221
+ Ok ( db)
278
222
}
279
223
}
280
224
0 commit comments