@@ -97,3 +97,274 @@ impl Limits {
9797 }
9898 }
9999}
100+
101+ /// Convinience trait that allows API users to either used a factory type or a
102+ /// concrete limits value in [`Pipeline::with_limits`].
103+ pub trait IntoLimitsFactory < P : Platform , Marker = ( ) > {
104+ /// Convert the type into a limits factory.
105+ fn into_limits_factory ( self ) -> impl LimitsFactory < P > ;
106+ }
107+
108+ impl < T : LimitsFactory < P > , P : Platform > IntoLimitsFactory < P , Variant < 0 > > for T {
109+ fn into_limits_factory ( self ) -> impl LimitsFactory < P > {
110+ self
111+ }
112+ }
113+
114+ impl < P : Platform > IntoLimitsFactory < P , Variant < 1 > > for Limits {
115+ fn into_limits_factory ( self ) -> impl LimitsFactory < P > {
116+ struct FixedLimits ( Limits ) ;
117+ impl < P : Platform > LimitsFactory < P > for FixedLimits {
118+ fn create ( & self , _: & BlockContext < P > , _: Option < & Limits > ) -> Limits {
119+ self . 0 . clone ( )
120+ }
121+ }
122+
123+ FixedLimits ( self )
124+ }
125+ }
126+
127+ #[ cfg( test) ]
128+ mod tests {
129+ use {
130+ super :: * ,
131+ crate :: test_utils:: * ,
132+ core:: {
133+ num:: NonZero ,
134+ sync:: atomic:: { AtomicU32 , Ordering } ,
135+ } ,
136+ std:: sync:: Arc ,
137+ } ;
138+
139+ assert_is_dyn_safe ! ( LimitsFactory <P >, P : Platform ) ;
140+
141+ #[ derive( Debug ) ]
142+ struct TestStep {
143+ must_run : bool ,
144+ iter : AtomicU32 ,
145+ break_after : AtomicU32 ,
146+
147+ minimum_iterations : Option < u32 > ,
148+ maximum_iterations : Option < u32 > ,
149+ expected_gas_limit : Option < u64 > ,
150+ expected_deadline : Option < Duration > ,
151+ sleep_on_step : Option < Duration > ,
152+ }
153+
154+ impl Default for TestStep {
155+ fn default ( ) -> Self {
156+ Self {
157+ must_run : false ,
158+ iter : AtomicU32 :: new ( 0 ) ,
159+ break_after : AtomicU32 :: new ( 1 ) ,
160+ expected_gas_limit : None ,
161+ expected_deadline : None ,
162+ minimum_iterations : None ,
163+ maximum_iterations : None ,
164+ sleep_on_step : None ,
165+ }
166+ }
167+ }
168+
169+ impl TestStep {
170+ #[ allow( dead_code) ]
171+ pub fn break_after ( self , iterations : u32 ) -> Self {
172+ self . break_after . store ( iterations, Ordering :: Relaxed ) ;
173+ self
174+ }
175+
176+ pub fn expect_gas_limit ( mut self , gas_limit : u64 ) -> Self {
177+ self . expected_gas_limit = Some ( gas_limit) ;
178+ self
179+ }
180+
181+ pub fn expect_deadline ( mut self , deadline : Duration ) -> Self {
182+ self . expected_deadline = Some ( deadline) ;
183+ self
184+ }
185+
186+ pub fn expect_minimum_iterations ( mut self , iterations : u32 ) -> Self {
187+ self . minimum_iterations = Some ( iterations) ;
188+ self
189+ }
190+
191+ pub fn expect_maximum_iterations ( mut self , iterations : u32 ) -> Self {
192+ self . maximum_iterations = Some ( iterations) ;
193+ self
194+ }
195+
196+ pub fn sleep_on_step ( mut self , duration : Duration ) -> Self {
197+ self . sleep_on_step = Some ( duration) ;
198+ self
199+ }
200+
201+ #[ track_caller]
202+ pub fn must_run ( mut self ) -> Self {
203+ self . must_run = true ;
204+ self
205+ }
206+ }
207+
208+ impl Drop for TestStep {
209+ fn drop ( & mut self ) {
210+ assert ! (
211+ !self . must_run || self . iter. load( Ordering :: Relaxed ) > 0 ,
212+ "Step was expected to run, but did not."
213+ ) ;
214+
215+ if let Some ( minimum_iterations) = self . minimum_iterations {
216+ assert ! (
217+ self . iter. load( Ordering :: Relaxed ) >= minimum_iterations,
218+ "Expected step to run at least {minimum_iterations} times, but ran \
219+ {} times.",
220+ self . iter. load( Ordering :: Relaxed )
221+ ) ;
222+ }
223+
224+ if let Some ( maximum_iterations) = self . maximum_iterations {
225+ assert ! (
226+ self . iter. load( Ordering :: Relaxed ) <= maximum_iterations,
227+ "Expected step to run at most {maximum_iterations} times, but ran \
228+ {} times.",
229+ self . iter. load( Ordering :: Relaxed )
230+ ) ;
231+ }
232+ }
233+ }
234+
235+ impl < P : Platform > Step < P > for TestStep {
236+ async fn step (
237+ self : Arc < Self > ,
238+ payload : Checkpoint < P > ,
239+ ctx : StepContext < P > ,
240+ ) -> ControlFlow < P > {
241+ if let Some ( sleep_duration) = self . sleep_on_step {
242+ tokio:: time:: sleep ( sleep_duration) . await ;
243+ }
244+
245+ let break_after = self . break_after . load ( Ordering :: Relaxed ) ;
246+ if self . iter . fetch_add ( 1 , Ordering :: Relaxed ) >= break_after {
247+ return ControlFlow :: Break ( payload) ;
248+ }
249+
250+ if ctx. deadline_reached ( ) {
251+ return ControlFlow :: Break ( payload) ;
252+ }
253+
254+ if let Some ( expected_gas_limit) = self . expected_gas_limit {
255+ assert_eq ! (
256+ ctx. limits( ) . gas_limit,
257+ expected_gas_limit,
258+ "Expected gas limit to be {expected_gas_limit}, but got {}" ,
259+ ctx. limits( ) . gas_limit
260+ ) ;
261+ }
262+
263+ if let Some ( expected_deadline) = self . expected_deadline {
264+ assert_eq ! (
265+ ctx. limits( ) . deadline,
266+ Some ( expected_deadline) ,
267+ "Expected deadline to be {expected_deadline:?}, but got {:?}" ,
268+ ctx. limits( ) . deadline
269+ ) ;
270+ }
271+
272+ ControlFlow :: Ok ( payload)
273+ }
274+ }
275+
276+ #[ rblib_test( Ethereum , Optimism ) ]
277+ async fn one_level_flat < P : TestablePlatform > ( ) -> eyre:: Result < ( ) > {
278+ let pipeline = Pipeline :: < P > :: default ( )
279+ . with_step (
280+ TestStep :: default ( )
281+ . expect_gas_limit ( 1000 )
282+ . expect_deadline ( Duration :: from_millis ( 500 ) )
283+ . must_run ( ) ,
284+ )
285+ . with_limits (
286+ Limits :: with_gas_limit ( 1000 ) . with_deadline ( Duration :: from_millis ( 500 ) ) ,
287+ ) ;
288+ P :: create_test_node ( pipeline) . await ?. next_block ( ) . await ?;
289+ Ok ( ( ) )
290+ }
291+
292+ #[ rblib_test( Ethereum , Optimism ) ]
293+ async fn scale_fraction < P : TestablePlatform > ( ) -> eyre:: Result < ( ) > {
294+ let pipeline = Pipeline :: < P > :: default ( )
295+ . with_pipeline (
296+ Once ,
297+ ( TestStep :: default ( )
298+ . expect_gas_limit ( 500 )
299+ . expect_deadline ( Duration :: from_millis ( 250 ) )
300+ . must_run ( ) , )
301+ . with_limits (
302+ Scaled :: default ( )
303+ . gas ( Fraction ( 1 , NonZero :: new ( 2 ) . unwrap ( ) ) )
304+ . deadline ( Fraction ( 1 , NonZero :: new ( 2 ) . unwrap ( ) ) ) ,
305+ ) ,
306+ )
307+ . with_limits (
308+ Limits :: with_gas_limit ( 1000 ) . with_deadline ( Duration :: from_millis ( 500 ) ) ,
309+ ) ;
310+ P :: create_test_node ( pipeline) . await ?. next_block ( ) . await ?;
311+ Ok ( ( ) )
312+ }
313+
314+ #[ rblib_test( Ethereum , Optimism ) ]
315+ async fn scale_minus < P : TestablePlatform > ( ) -> eyre:: Result < ( ) > {
316+ let pipeline = Pipeline :: < P > :: default ( )
317+ . with_pipeline (
318+ Once ,
319+ ( TestStep :: default ( )
320+ . expect_gas_limit ( 900 )
321+ . expect_deadline ( Duration :: from_millis ( 400 ) )
322+ . must_run ( ) , )
323+ . with_limits (
324+ Scaled :: default ( )
325+ . gas ( Minus ( 100 ) )
326+ . deadline ( Minus ( Duration :: from_millis ( 100 ) ) ) ,
327+ ) ,
328+ )
329+ . with_limits (
330+ Limits :: with_gas_limit ( 1000 ) . with_deadline ( Duration :: from_millis ( 500 ) ) ,
331+ ) ;
332+ P :: create_test_node ( pipeline) . await ?. next_block ( ) . await ?;
333+ Ok ( ( ) )
334+ }
335+
336+ /// two levels of scaling
337+ #[ rblib_test( Ethereum , Optimism ) ]
338+ async fn deadline_approaches < P : TestablePlatform > ( ) -> eyre:: Result < ( ) > {
339+ let pipeline = Pipeline :: < P > :: default ( )
340+ . with_pipeline (
341+ Once ,
342+ Pipeline :: default ( )
343+ . with_pipeline (
344+ Once ,
345+ ( TestStep :: default ( )
346+ . expect_gas_limit ( 400 )
347+ . expect_deadline ( Duration :: from_millis ( 300 ) )
348+ . expect_minimum_iterations ( 2 )
349+ . expect_maximum_iterations ( 3 )
350+ . sleep_on_step ( Duration :: from_millis ( 100 ) )
351+ . must_run ( ) , )
352+ . with_limits (
353+ Scaled :: default ( )
354+ . gas ( Minus ( 100 ) )
355+ . deadline ( Minus ( Duration :: from_millis ( 100 ) ) ) ,
356+ ) ,
357+ )
358+ . with_limits (
359+ Scaled :: default ( )
360+ . gas ( Fraction ( 1 , NonZero :: new ( 2 ) . unwrap ( ) ) )
361+ . deadline ( Fraction ( 1 , NonZero :: new ( 2 ) . unwrap ( ) ) ) ,
362+ ) ,
363+ )
364+ . with_limits (
365+ Limits :: with_gas_limit ( 1000 ) . with_deadline ( Duration :: from_millis ( 800 ) ) ,
366+ ) ;
367+ P :: create_test_node ( pipeline) . await ?. next_block ( ) . await ?;
368+ Ok ( ( ) )
369+ }
370+ }
0 commit comments