@@ -175,40 +175,12 @@ impl ProofAggregator {
175175 hex:: encode( blob_versioned_hash)
176176 ) ;
177177
178- // We start on 24 hours because the proof aggregator runs once a day, so the time elapsed
179- // should be considered over a 24h period.
180- let mut time_elapsed = Duration :: from_secs ( 24 * 3600 ) ;
181-
182- // Iterate until we can send the proof on-chain
183- loop {
184- // Fetch gas price from network
185- let gas_price = self
186- . rpc_provider
187- . get_gas_price ( )
188- . await
189- . map_err ( |e| AggregatedProofSubmissionError :: GasPriceError ( e. to_string ( ) ) ) ?;
190-
191- if Self :: should_send_proof_to_verify_on_chain (
192- time_elapsed,
193- self . config . monthly_budget_eth ,
194- U256 :: from ( gas_price) ,
195- ) {
196- break ;
197- } else {
198- info ! ( "Skipping sending proof to ProofAggregationService contract due to budget/time constraints." ) ;
199- }
200-
201- // Sleep for 3 minutes (15 blocks) before re-evaluating
202- let time_to_sleep = Duration :: from_secs ( 180 ) ;
203- time_elapsed += time_to_sleep;
204- sleep ( time_to_sleep) ;
205- }
206-
207- info ! ( "Sending proof to ProofAggregationService contract..." ) ;
208-
209- // Retry in case of failure
210178 let receipt = self
211- . send_proof_to_verify_on_chain_retryable ( blob, blob_versioned_hash, aggregated_proof)
179+ . bump_and_send_proof_to_verify_on_chain_retryable (
180+ blob,
181+ blob_versioned_hash,
182+ aggregated_proof,
183+ )
212184 . await ?;
213185 info ! (
214186 "Proof sent and verified, tx hash {:?}" ,
@@ -239,84 +211,35 @@ impl ProofAggregator {
239211 Ok ( ( ) )
240212 }
241213
242- fn max_to_spend_in_wei ( time_elapsed : Duration , monthly_eth_budget : f64 ) -> U256 {
243- const SECONDS_PER_MONTH : u64 = 30 * 24 * 60 * 60 ;
244-
245- // Note: this expect is safe because in case it was invalid, should have been caught at startup
246- let monthly_budget_in_wei = parse_ether ( & monthly_eth_budget. to_string ( ) )
247- . expect ( "The monthly budget should be a non-negative value" ) ;
248-
249- let elapsed_seconds = U256 :: from ( time_elapsed. as_secs ( ) ) ;
250-
251- let budget_available_per_second_in_wei =
252- monthly_budget_in_wei / U256 :: from ( SECONDS_PER_MONTH ) ;
253-
254- budget_available_per_second_in_wei * elapsed_seconds
255- }
256-
257- /// Decides whether to send the aggregated proof to be verified on-chain based on
258- /// time elapsed since last submission and monthly ETH budget.
259- /// We make a linear function with the eth to spend this month and the time elapsed since last submission.
260- /// If eth to spend / elapsed time is over the linear function, we skip the submission.
261- fn should_send_proof_to_verify_on_chain (
262- time_elapsed : Duration ,
263- monthly_eth_budget : f64 ,
264- network_gas_price : U256 ,
265- ) -> bool {
266- // We assume a fixed gas cost of 300,000 for each of the 2 transactions
267- const ON_CHAIN_COST_IN_GAS_UNITS : u64 = 600_000u64 ;
268-
269- let on_chain_cost_in_gas: U256 = U256 :: from ( ON_CHAIN_COST_IN_GAS_UNITS ) ;
270- let max_to_spend_in_wei = Self :: max_to_spend_in_wei ( time_elapsed, monthly_eth_budget) ;
271-
272- let expected_cost_in_wei = network_gas_price * on_chain_cost_in_gas;
273-
274- expected_cost_in_wei <= max_to_spend_in_wei
275- }
276-
277- async fn send_proof_to_verify_on_chain_retryable (
214+ async fn bump_and_send_proof_to_verify_on_chain_retryable (
278215 & self ,
279216 blob : BlobTransactionSidecar ,
280217 blob_versioned_hash : [ u8 ; 32 ] ,
281218 aggregated_proof : AlignedProof ,
282219 ) -> Result < TransactionReceipt , AggregatedProofSubmissionError > {
283- match send_proof_to_verify_on_chain (
284- blob. clone ( ) ,
285- blob_versioned_hash,
286- aggregated_proof. clone ( ) ,
287- self . proof_aggregation_service . clone ( ) ,
288- self . sp1_chunk_aggregator_vk_hash_bytes ,
289- self . risc0_chunk_aggregator_image_id_bytes ,
220+ retry_function (
221+ || {
222+ bump_and_send_proof_to_verify_on_chain (
223+ blob. clone ( ) ,
224+ blob_versioned_hash,
225+ aggregated_proof. clone ( ) ,
226+ self . proof_aggregation_service . clone ( ) ,
227+ self . sp1_chunk_aggregator_vk_hash_bytes ,
228+ self . risc0_chunk_aggregator_image_id_bytes ,
229+ self . rpc_provider . clone ( ) ,
230+ self . config . monthly_budget_eth ,
231+ )
232+ } ,
233+ ETHEREUM_CALL_MIN_RETRY_DELAY ,
234+ ETHEREUM_CALL_BACKOFF_FACTOR ,
235+ ETHEREUM_CALL_MAX_RETRIES ,
236+ ETHEREUM_CALL_MAX_RETRY_DELAY ,
290237 )
291238 . await
292- {
293- Ok ( tx_receipt) => Ok ( tx_receipt) ,
294- Err ( err) => {
295- tracing:: error!( "Failed to send proof to be verified on chain: {err:?}" ) ;
296-
297- retry_function (
298- || {
299- send_proof_to_verify_on_chain (
300- blob. clone ( ) ,
301- blob_versioned_hash,
302- aggregated_proof. clone ( ) ,
303- self . proof_aggregation_service . clone ( ) ,
304- self . sp1_chunk_aggregator_vk_hash_bytes ,
305- self . risc0_chunk_aggregator_image_id_bytes ,
306- )
307- } ,
308- ETHEREUM_CALL_MIN_RETRY_DELAY ,
309- ETHEREUM_CALL_BACKOFF_FACTOR ,
310- ETHEREUM_CALL_MAX_RETRIES ,
311- ETHEREUM_CALL_MAX_RETRY_DELAY ,
312- )
313- . await
314- . map_err ( |e| {
315- error ! ( "Could't get nonce: {:?}" , e) ;
316- e. inner ( )
317- } )
318- }
319- }
239+ . map_err ( |e| {
240+ error ! ( "Could't get nonce: {:?}" , e) ;
241+ e. inner ( )
242+ } )
320243 }
321244
322245 /// ### Blob capacity
@@ -392,14 +315,45 @@ impl ProofAggregator {
392315 }
393316}
394317
395- async fn send_proof_to_verify_on_chain (
318+ async fn bump_and_send_proof_to_verify_on_chain (
396319 blob : BlobTransactionSidecar ,
397320 blob_versioned_hash : [ u8 ; 32 ] ,
398321 aggregated_proof : AlignedProof ,
399322 proof_aggregation_service : AlignedProofAggregationServiceContract ,
400323 sp1_chunk_aggregator_vk_hash_bytes : [ u8 ; 32 ] ,
401324 risc0_chunk_aggregator_image_id_bytes : [ u8 ; 32 ] ,
325+ rpc_provider : RPCProvider ,
326+ monthly_budget_eth : f64 ,
402327) -> Result < TransactionReceipt , RetryError < AggregatedProofSubmissionError > > {
328+ // We start on 24 hours because the proof aggregator runs once a day, so the time elapsed
329+ // should be considered over a 24h period.
330+ let mut time_elapsed = Duration :: from_secs ( 24 * 3600 ) ;
331+
332+ // Iterate until we can send the proof on-chain
333+ loop {
334+ // Fetch gas price from network
335+ let gas_price = rpc_provider. get_gas_price ( ) . await . map_err ( |e| {
336+ RetryError :: Transient ( AggregatedProofSubmissionError :: GasPriceError ( e. to_string ( ) ) )
337+ } ) ?;
338+
339+ if should_send_proof_to_verify_on_chain (
340+ time_elapsed,
341+ monthly_budget_eth,
342+ U256 :: from ( gas_price) ,
343+ ) {
344+ break ;
345+ } else {
346+ info ! ( "Skipping sending proof to ProofAggregationService contract due to budget/time constraints." ) ;
347+ }
348+
349+ // Sleep for 3 minutes (15 blocks) before re-evaluating
350+ let time_to_sleep = Duration :: from_secs ( 180 ) ;
351+ time_elapsed += time_to_sleep;
352+ sleep ( time_to_sleep) ;
353+ }
354+
355+ info ! ( "Sending proof to ProofAggregationService contract..." ) ;
356+
403357 let tx_req = match aggregated_proof {
404358 AlignedProof :: SP1 ( proof) => proof_aggregation_service
405359 . verifyAggregationSP1 (
@@ -473,6 +427,40 @@ async fn send_proof_to_verify_on_chain(
473427 Ok ( receipt)
474428}
475429
430+ /// Decides whether to send the aggregated proof to be verified on-chain based on
431+ /// time elapsed since last submission and monthly ETH budget.
432+ /// We make a linear function with the eth to spend this month and the time elapsed since last submission.
433+ /// If eth to spend / elapsed time is over the linear function, we skip the submission.
434+ fn should_send_proof_to_verify_on_chain (
435+ time_elapsed : Duration ,
436+ monthly_eth_budget : f64 ,
437+ network_gas_price : U256 ,
438+ ) -> bool {
439+ // We assume a fixed gas cost of 300,000 for each of the 2 transactions
440+ const ON_CHAIN_COST_IN_GAS_UNITS : u64 = 600_000u64 ;
441+
442+ let on_chain_cost_in_gas: U256 = U256 :: from ( ON_CHAIN_COST_IN_GAS_UNITS ) ;
443+ let max_to_spend_in_wei = max_to_spend_in_wei ( time_elapsed, monthly_eth_budget) ;
444+
445+ let expected_cost_in_wei = network_gas_price * on_chain_cost_in_gas;
446+
447+ expected_cost_in_wei <= max_to_spend_in_wei
448+ }
449+
450+ fn max_to_spend_in_wei ( time_elapsed : Duration , monthly_eth_budget : f64 ) -> U256 {
451+ const SECONDS_PER_MONTH : u64 = 30 * 24 * 60 * 60 ;
452+
453+ // Note: this expect is safe because in case it was invalid, should have been caught at startup
454+ let monthly_budget_in_wei = parse_ether ( & monthly_eth_budget. to_string ( ) )
455+ . expect ( "The monthly budget should be a non-negative value" ) ;
456+
457+ let elapsed_seconds = U256 :: from ( time_elapsed. as_secs ( ) ) ;
458+
459+ let budget_available_per_second_in_wei = monthly_budget_in_wei / U256 :: from ( SECONDS_PER_MONTH ) ;
460+
461+ budget_available_per_second_in_wei * elapsed_seconds
462+ }
463+
476464use backon:: ExponentialBuilder ;
477465use backon:: Retryable ;
478466use std:: future:: Future ;
@@ -554,7 +542,7 @@ mod tests {
554542 // Max to spend: 0.000000058 ETH/hour * 24 hours = 0.005 ETH
555543 // Expected cost: 600,000 * 1 Gwei = 0.0006 ETH
556544 // Expected cost < Max to spend, so we can send the proof
557- assert ! ( ProofAggregator :: should_send_proof_to_verify_on_chain(
545+ assert ! ( should_send_proof_to_verify_on_chain(
558546 Duration :: from_secs( ONE_DAY_SECONDS ) , // 24 hours
559547 BUDGET_PER_MONTH_IN_ETH , // 0.15 ETH monthly budget
560548 gas_price, // 1 Gwei gas price
@@ -567,7 +555,7 @@ mod tests {
567555 // Max to spend: 0.000000058 ETH/hour * 24 hours = 0.005 ETH
568556 // Expected cost: 600,000 * 8 Gwei = 0.0048 ETH
569557 // Expected cost < Max to spend, so we can send the proof
570- assert ! ( ProofAggregator :: should_send_proof_to_verify_on_chain(
558+ assert ! ( should_send_proof_to_verify_on_chain(
571559 Duration :: from_secs( ONE_DAY_SECONDS ) , // 24 hours
572560 BUDGET_PER_MONTH_IN_ETH , // 0.15 ETH monthly budget
573561 U256 :: from( 8_000_000_000u64 ) , // 8 Gwei gas price
@@ -580,7 +568,7 @@ mod tests {
580568 // Max to spend: 0.000000058 ETH/hour * 24 hours = 0.005 ETH
581569 // Expected cost: 600,000 * 10 Gwei = 0.006 ETH
582570 // Expected cost > Max to spend, so we cannot send the proof
583- assert ! ( !ProofAggregator :: should_send_proof_to_verify_on_chain(
571+ assert ! ( !should_send_proof_to_verify_on_chain(
584572 Duration :: from_secs( ONE_DAY_SECONDS ) , // 24 hours
585573 BUDGET_PER_MONTH_IN_ETH , // 0.15 ETH monthly budget
586574 U256 :: from( 10_000_000_000u64 ) , // 10 Gwei gas price
@@ -593,7 +581,7 @@ mod tests {
593581 // Max to spend: 0.000000058 ETH/hour * 3 hours = 0.000625 ETH
594582 // Expected cost: 600,000 * 1 Gwei = 0.0006 ETH
595583 // Expected cost < Max to spend, so we can send the proof
596- assert ! ( ProofAggregator :: should_send_proof_to_verify_on_chain(
584+ assert ! ( should_send_proof_to_verify_on_chain(
597585 Duration :: from_secs( 3 * 3600 ) , // 3 hours
598586 BUDGET_PER_MONTH_IN_ETH , // 0.15 ETH monthly budget
599587 gas_price, // 1 Gwei gas price
@@ -606,7 +594,7 @@ mod tests {
606594 // Max to spend: 0.000000058 ETH/hour * 1.2 hours = 0.00025 ETH
607595 // Expected cost: 600,000 * 1 Gwei = 0.0006 ETH
608596 // Expected cost > Max to spend, so we cannot send the proof
609- assert ! ( !ProofAggregator :: should_send_proof_to_verify_on_chain(
597+ assert ! ( !should_send_proof_to_verify_on_chain(
610598 Duration :: from_secs_f64( 1.2 * 3600.0 ) , // 1.2 hours
611599 BUDGET_PER_MONTH_IN_ETH , // 0.15 ETH monthly budget
612600 gas_price, // 1 Gwei gas price
@@ -619,7 +607,7 @@ mod tests {
619607 // Max to spend: 0.000000038 ETH/hour * 24 hours = 0.0032832 ETH
620608 // Expected cost: 600,000 * 1 Gwei = 0.0006 ETH
621609 // Expected cost < Max to spend, so we can send the proof
622- assert ! ( ProofAggregator :: should_send_proof_to_verify_on_chain(
610+ assert ! ( should_send_proof_to_verify_on_chain(
623611 Duration :: from_secs( ONE_DAY_SECONDS ) , // 24 hours
624612 0.1 , // 0.1 ETH monthly budget
625613 gas_price, // 1 Gwei gas price
@@ -632,7 +620,7 @@ mod tests {
632620 // Max to spend: 0.0000000038 ETH/hour * 24 hours = 0.00032832 ETH
633621 // Expected cost: 600,000 * 1 Gwei = 0.0006 ETH
634622 // Expected cost > Max to spend, so we cannot send the proof
635- assert ! ( !ProofAggregator :: should_send_proof_to_verify_on_chain(
623+ assert ! ( !should_send_proof_to_verify_on_chain(
636624 Duration :: from_secs( ONE_DAY_SECONDS ) , // 24 hours
637625 0.01 , // 0.01 ETH monthly budget
638626 gas_price, // 1 Gwei gas price
0 commit comments