@@ -198,7 +198,7 @@ def kzg_to_versioned_hash(kzg_commitment):
198198 return "0x01" + sha256 (bytes .fromhex (kzg_commitment [2 :])).hexdigest ()[2 :]
199199
200200
201- def latest_finalized_event_block (width = 5 ):
201+ def latest_finalized_event_block (width = 1000 ):
202202 """Find the latest L1 block with a FinalizeBatch event"""
203203 finalized_l1_head = - 1
204204
@@ -520,7 +520,7 @@ def parse_batch(blob_hash, block_number, mainnet_beacon_url, cur_slot, cur_block
520520# DATA COLLECTION FUNCTIONS
521521# ============================================================================
522522
523- def collect_batch_data (n_batches = 30 , width = 5 , start_time = None ):
523+ def collect_batch_data (n_batches = 30 , width = 1000 , start_time = None ):
524524 """
525525 Collect batch data from L1 (commits and finalizations)
526526
@@ -686,19 +686,17 @@ def collect_batch_data(n_batches=30, width=5, start_time=None):
686686
687687 # Step 2: Now collect commit data for these specific batches
688688 print (f"\n Step 2: Collecting commit data for finalized batches..." )
689- batch_data_dict = {} # batch_index -> [versioned_hash, initial_L2_block_number, num_blocks, commit_cost, blob_cost]
690- to_block = l1_head
691- from_block = to_block - width
692- cur_slot = beacon_head_slot
693- cur_block = l1_head
694- prev_batch_count = 0
695689
696- # We need to find commits for all batches in finalized_batch_indices
690+ # Phase 1: Scan all CommitBatch events and record metadata
691+ print (f" Phase 1: Scanning CommitBatch events..." )
697692 target_batches = set (finalized_batch_indices )
693+ batch_events = {} # batch_index -> {tx_hash, blob_index}
698694 last_tx_hash = None
699695 blob_index = 0
696+ to_block = l1_head
697+ from_block = to_block - width
700698
701- while len (batch_data_dict ) < n_batches :
699+ while len (batch_events ) < n_batches :
702700 event_filter = rollup_contract .events .CommitBatch .create_filter (fromBlock = from_block , toBlock = to_block )
703701 events = event_filter .get_all_entries ()
704702
@@ -707,53 +705,77 @@ def collect_batch_data(n_batches=30, width=5, start_time=None):
707705 tx_hash = event .transactionHash
708706
709707 # Track blob index for multiple batches in same transaction
710- # This must be done BEFORE filtering, so we count all batches in the tx
711708 if last_tx_hash == tx_hash :
712709 blob_index += 1
713710 else :
714711 last_tx_hash = tx_hash
715712 blob_index = 0
716713
717- # Only process if this batch is in our finalized list
718- if batch_index not in target_batches :
719- continue
714+ if batch_index in target_batches and batch_index not in batch_events :
715+ batch_events [batch_index ] = {'tx_hash' : tx_hash , 'blob_index' : blob_index }
720716
721- # Skip if we already have this batch
722- if batch_index in batch_data_dict :
723- continue
717+ to_block = from_block - 1
718+ from_block = from_block - width - 1
719+ if from_block < 0 :
720+ break
724721
725- tx = w3 .eth .get_transaction (event .transactionHash )
726- receipt = w3 .eth .get_transaction_receipt (event .transactionHash )
722+ print (f" Found { len (batch_events )} CommitBatch events" )
727723
728- block_number = tx .blockNumber
729- num_batches_in_tx = len (tx .blobVersionedHashes )
730- blob_hash = tx .blobVersionedHashes [blob_index ].hex ()
724+ # Phase 2: Parallel fetch tx + receipt for unique tx hashes
725+ print (f" Phase 2: Fetching transactions and receipts in parallel..." )
726+ unique_tx_hashes = list (set (e ['tx_hash' ] for e in batch_events .values ()))
727+ print (f" Unique transactions to fetch: { len (unique_tx_hashes )} " )
731728
732- # Calculate commit cost (execution gas only, amortized across all batches in this tx)
733- commit_cost = (receipt ['gasUsed' ] * receipt ['effectiveGasPrice' ]) / num_batches_in_tx
729+ tx_cache = {} # tx_hash -> (tx, receipt)
734730
735- # Parse batch to get L2 block range
736- initial_L2_block_number , num_blocks , cur_slot , cur_block = parse_batch (
737- blob_hash , block_number , mainnet_beacon_url , cur_slot , cur_block
738- )
731+ def fetch_tx_and_receipt ( tx_hash ):
732+ tx = w3 . eth . get_transaction ( tx_hash )
733+ receipt = w3 . eth . get_transaction_receipt ( tx_hash )
734+ return tx_hash , tx , receipt
739735
740- # Calculate blob cost (amortized across all batches in this tx)
741- blob_cost = (receipt ['blobGasPrice' ] * receipt ['blobGasUsed' ]) / num_batches_in_tx
736+ max_workers = 20
737+ with ThreadPoolExecutor (max_workers = max_workers ) as executor :
738+ futures = {executor .submit (fetch_tx_and_receipt , h ): h for h in unique_tx_hashes }
739+ completed = 0
740+ for future in as_completed (futures ):
741+ tx_hash , tx , receipt = future .result ()
742+ tx_cache [tx_hash ] = (tx , receipt )
743+ completed += 1
744+ if completed % 50 == 0 or completed == len (unique_tx_hashes ):
745+ print (f" Fetched { completed } /{ len (unique_tx_hashes )} transactions" )
742746
743- batch_data_dict [batch_index ] = [blob_hash , initial_L2_block_number , num_blocks ,
744- commit_cost , blob_cost ]
747+ print (f" Done fetching transactions" )
745748
746- # Only print when count changes
747- if len (batch_data_dict ) > prev_batch_count :
748- print (f" Collected commit data for { len (batch_data_dict )} /{ n_batches } batches" )
749- prev_batch_count = len (batch_data_dict )
749+ # Phase 3: Parse blob data for each batch (sequential, needs slot state)
750+ print (f" Phase 3: Parsing blob data..." )
751+ batch_data_dict = {}
752+ cur_slot = beacon_head_slot
753+ cur_block = l1_head
750754
751- to_block = from_block - 1
752- from_block = from_block - width - 1
755+ # Process in block_number descending order for slot tracking
756+ sorted_batches = sorted ( batch_events . items (), key = lambda x : tx_cache [ x [ 1 ][ 'tx_hash' ]][ 0 ]. blockNumber , reverse = True )
753757
754- # Stop if we've searched far enough
755- if from_block < 0 :
756- break
758+ for i , (batch_index , event_info ) in enumerate (sorted_batches ):
759+ tx , receipt = tx_cache [event_info ['tx_hash' ]]
760+ blob_idx = event_info ['blob_index' ]
761+
762+ block_number = tx .blockNumber
763+ num_batches_in_tx = len (tx .blobVersionedHashes )
764+ blob_hash = tx .blobVersionedHashes [blob_idx ].hex ()
765+
766+ commit_cost = (receipt ['gasUsed' ] * receipt ['effectiveGasPrice' ]) / num_batches_in_tx
767+
768+ initial_L2_block_number , num_blocks , cur_slot , cur_block = parse_batch (
769+ blob_hash , block_number , mainnet_beacon_url , cur_slot , cur_block
770+ )
771+
772+ blob_cost = (receipt ['blobGasPrice' ] * receipt ['blobGasUsed' ]) / num_batches_in_tx
773+
774+ batch_data_dict [batch_index ] = [blob_hash , initial_L2_block_number , num_blocks ,
775+ commit_cost , blob_cost ]
776+
777+ if (i + 1 ) % 50 == 0 or (i + 1 ) == len (sorted_batches ):
778+ print (f" Parsed { i + 1 } /{ len (sorted_batches )} batches" )
757779
758780 # Step 3: Combine commit and finalize data
759781 print (f"\n Step 3: Combining commit and finalize data..." )
@@ -1398,6 +1420,32 @@ def compare_parameters(current_params, commit_scalar, blob_scalar, penalty_multi
13981420 print ("PARAMETER COMPARISON ANALYSIS" )
13991421 print ("=" * 60 )
14001422
1423+ # L1 Gas Price Distribution
1424+ print ("\n " + "-" * 60 )
1425+ print ("L1 Gas Price Distribution (as seen by L2 fee oracle)" )
1426+ print ("-" * 60 )
1427+
1428+ l1_base = tx_df ['l1_base_fee' ]
1429+ l1_blob = tx_df ['l1_blob_base_fee' ]
1430+
1431+ print (f"\n l1_base_fee (wei):" )
1432+ print (f" Min: { l1_base .min ():>15,} ({ l1_base .min ()/ 1e9 :.4f} gwei)" )
1433+ print (f" P5: { l1_base .quantile (0.05 ):>15,.0f} ({ l1_base .quantile (0.05 )/ 1e9 :.4f} gwei)" )
1434+ print (f" Median: { l1_base .median ():>15,.0f} ({ l1_base .median ()/ 1e9 :.4f} gwei)" )
1435+ print (f" Mean: { l1_base .mean ():>15,.0f} ({ l1_base .mean ()/ 1e9 :.4f} gwei)" )
1436+ print (f" P95: { l1_base .quantile (0.95 ):>15,.0f} ({ l1_base .quantile (0.95 )/ 1e9 :.4f} gwei)" )
1437+ print (f" Max: { l1_base .max ():>15,} ({ l1_base .max ()/ 1e9 :.4f} gwei)" )
1438+ print (f" Max/Min ratio: { l1_base .max ()/ l1_base .min ():.1f} x" )
1439+
1440+ print (f"\n l1_blob_base_fee (wei):" )
1441+ print (f" Min: { l1_blob .min ():>15,} ({ l1_blob .min ()/ 1e9 :.4f} gwei)" )
1442+ print (f" P5: { l1_blob .quantile (0.05 ):>15,.0f} ({ l1_blob .quantile (0.05 )/ 1e9 :.4f} gwei)" )
1443+ print (f" Median: { l1_blob .median ():>15,.0f} ({ l1_blob .median ()/ 1e9 :.4f} gwei)" )
1444+ print (f" Mean: { l1_blob .mean ():>15,.0f} ({ l1_blob .mean ()/ 1e9 :.4f} gwei)" )
1445+ print (f" P95: { l1_blob .quantile (0.95 ):>15,.0f} ({ l1_blob .quantile (0.95 )/ 1e9 :.4f} gwei)" )
1446+ print (f" Max: { l1_blob .max ():>15,} ({ l1_blob .max ()/ 1e9 :.4f} gwei)" )
1447+ print (f" Max/Min ratio: { l1_blob .max ()/ l1_blob .min ():.1f} x" )
1448+
14011449 # Extract current (old) parameters
14021450 old_commit_scalar = current_params ['commit_scalar' ]
14031451 old_blob_scalar = current_params ['blob_scalar' ]
0 commit comments