diff --git a/crates/ethereum-forks/src/hardfork.rs b/crates/ethereum-forks/src/hardfork.rs index e8930490f..0864e2a90 100644 --- a/crates/ethereum-forks/src/hardfork.rs +++ b/crates/ethereum-forks/src/hardfork.rs @@ -116,7 +116,7 @@ pub enum Hardfork { // ArbOS20Atlas, /// BSC `Haber` hardfork Haber, - /// BSC `Haber` hardfork + /// BSC `HaberFix` hardfork HaberFix, // Upcoming diff --git a/crates/stages/stages/src/stages/bodies.rs b/crates/stages/stages/src/stages/bodies.rs index 85b0e92aa..13a53aa40 100644 --- a/crates/stages/stages/src/stages/bodies.rs +++ b/crates/stages/stages/src/stages/bodies.rs @@ -283,7 +283,14 @@ impl Stage for BodyStage { } } } - BlockResponse::Empty(_) => {} + BlockResponse::Empty(header) => { + // Write empty sidecars + static_file_producer_sc.append_sidecars( + Default::default(), + block_number, + header.hash(), + )?; + } }; // insert block meta @@ -792,23 +799,34 @@ mod tests { // Insert last progress data { let tx = self.db.factory.provider_rw()?.into_tx(); - let mut static_file_producer = static_file_provider + let mut static_file_producer_tx = static_file_provider .get_writer(start, StaticFileSegment::Transactions)?; + let mut static_file_producer_sc = + static_file_provider.get_writer(start, StaticFileSegment::Sidecars)?; let body = StoredBlockBodyIndices { first_tx_num: 0, tx_count: progress.body.len() as u64, }; - static_file_producer.set_block_range(0..=progress.number); + static_file_producer_tx.set_block_range(0..=progress.number); body.tx_num_range().try_for_each(|tx_num| { let transaction = random_signed_tx(&mut rng); - static_file_producer + static_file_producer_tx .append_transaction(tx_num, transaction.into()) .map(|_| ()) })?; + for block_number in 0..=progress.number { + static_file_producer_sc.append_sidecars( + Default::default(), + block_number, + blocks.get(block_number as usize).map(|b| b.header.hash()).unwrap(), + )?; + tx.put::(block_number, Default::default())?; + } + if body.tx_count != 0 { tx.put::( body.last_tx_num(), @@ -825,7 +843,8 @@ mod tests { )?; } - static_file_producer.commit()?; + static_file_producer_tx.commit()?; + static_file_producer_sc.commit()?; tx.commit()?; } } diff --git a/crates/stages/stages/src/test_utils/test_db.rs b/crates/stages/stages/src/test_utils/test_db.rs index 8f72b5aab..b3e053f49 100644 --- a/crates/stages/stages/src/test_utils/test_db.rs +++ b/crates/stages/stages/src/test_utils/test_db.rs @@ -16,7 +16,7 @@ use reth_db_api::{ DatabaseError as DbError, }; use reth_primitives::{ - keccak256, Account, Address, BlockNumber, Receipt, SealedBlock, SealedHeader, + keccak256, Account, Address, BlockHash, BlockNumber, Receipt, SealedBlock, SealedHeader, StaticFileSegment, StorageEntry, TxHash, TxNumber, B256, U256, }; use reth_provider::{ @@ -171,6 +171,32 @@ impl TestStageDB { Ok(()) } + /// Insert sidecars to static file if `writer` exists, otherwise to DB. + /// Always insert empty data. + pub fn insert_sidecars( + writer: Option<&mut StaticFileProviderRWRefMut<'_>>, + tx: &TX, + hash: BlockHash, + block_number: BlockNumber, + ) -> ProviderResult<()> { + if let Some(writer) = writer { + // Backfill: some tests start at a forward block number, but static files require no + // gaps. + let segment_header = writer.user_header(); + if segment_header.block_end().is_none() && segment_header.expected_block_start() == 0 { + for block_number in 0..block_number { + writer.append_sidecars(Default::default(), block_number, B256::ZERO)?; + } + } + + writer.append_sidecars(Default::default(), block_number, hash)?; + } else { + tx.put::(block_number, Default::default())?; + } + + Ok(()) + } + fn insert_headers_inner<'a, I, const TD: bool>(&self, headers: I) -> ProviderResult<()> where I: IntoIterator, @@ -234,14 +260,26 @@ impl TestStageDB { let mut headers_writer = storage_kind .is_static() .then(|| provider.latest_writer(StaticFileSegment::Headers).unwrap()); + let mut sidecars_writer = storage_kind + .is_static() + .then(|| provider.latest_writer(StaticFileSegment::Sidecars).unwrap()); blocks.iter().try_for_each(|block| { + Self::insert_sidecars( + sidecars_writer.as_mut(), + &tx, + block.header.hash(), + block.header.number, + )?; Self::insert_header(headers_writer.as_mut(), &tx, &block.header, U256::ZERO) })?; if let Some(mut writer) = headers_writer { writer.commit()?; } + if let Some(mut writer) = sidecars_writer { + writer.commit()?; + } } { @@ -490,7 +528,7 @@ impl StorageKind { fn tx_offset(&self) -> u64 { if let Self::Database(offset) = self { - return offset.unwrap_or_default() + return offset.unwrap_or_default(); } 0 } diff --git a/crates/storage/provider/src/providers/static_file/writer.rs b/crates/storage/provider/src/providers/static_file/writer.rs index c3760863f..a1385580f 100644 --- a/crates/storage/provider/src/providers/static_file/writer.rs +++ b/crates/storage/provider/src/providers/static_file/writer.rs @@ -145,7 +145,9 @@ impl StaticFileProviderRW { })?; // If we have lost rows (in this run or previous), we need to update the [SegmentHeader]. - let expected_rows = if self.user_header().segment().is_headers() { + let expected_rows = if self.user_header().segment().is_headers() || + self.user_header().segment().is_sidecars() + { self.user_header().block_len().unwrap_or_default() } else { self.user_header().tx_len().unwrap_or_default() @@ -178,9 +180,7 @@ impl StaticFileProviderRW { StaticFileSegment::Receipts => { self.prune_receipt_data(to_delete, last_block_number.expect("should exist"))? } - StaticFileSegment::Sidecars => { - self.prune_sidecar_data(to_delete, last_block_number.expect("should exist"))? - } + StaticFileSegment::Sidecars => self.prune_sidecars_data(to_delete)?, } } @@ -750,17 +750,13 @@ impl StaticFileProviderRW { } /// Prunes the last `to_delete` sidecars from the data file. - fn prune_sidecar_data( - &mut self, - to_delete: u64, - last_block: BlockNumber, - ) -> ProviderResult<()> { + fn prune_sidecars_data(&mut self, to_delete: u64) -> ProviderResult<()> { let start = Instant::now(); let segment = StaticFileSegment::Sidecars; debug_assert!(self.writer.user_header().segment() == segment); - self.truncate(segment, to_delete, Some(last_block))?; + self.truncate(segment, to_delete, None)?; if let Some(metrics) = &self.metrics { metrics.record_segment_operation(