Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@

- [#7096](https://github.com/ChainSafe/forest/issues/7096): `eth_subscribe` `logs` now re-emits the logs of reorg-reverted tipsets with `removed: true`, ahead of the logs of the replacing tipsets.

- [#7156](https://github.com/ChainSafe/forest/pull/7156): The `eth` block `logsBloom` is now correctly computed from the block's logs instead of being all-ones. When the chain indexer is enabled, the computed bloom is cached in the database.

## Forest v0.33.7 "Shimmergloom"

### Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "forest-filecoin"
version = "0.33.7"
version = "0.33.8"
authors = ["ChainSafe Systems <info@chainsafe.io>"]
repository = "https://github.com/ChainSafe/forest"
edition = "2024"
Expand Down
3 changes: 0 additions & 3 deletions scripts/tests/api_compare/filter-list
Original file line number Diff line number Diff line change
@@ -1,5 +1,2 @@
# This list contains potentially broken methods (or tests) that are ignored.
# They should be considered bugged, and not used until the root cause is resolved.

!Filecoin.EthGetBlockByHash
!Filecoin.EthGetBlockByNumber
3 changes: 0 additions & 3 deletions scripts/tests/api_compare/filter-list-gateway
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,3 @@
# broken
# https://github.com/filecoin-project/lotus/pull/13562
!Filecoin.EthGetTransactionReceiptLimited
# https://github.com/filecoin-project/lotus/pull/13618
!Filecoin.EthGetBlockByHash
!Filecoin.EthGetBlockByNumber
23 changes: 21 additions & 2 deletions src/db/car/many.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ use super::{AnyCar, ZstdFrameCache};
use crate::blocks::TipsetKey;
use crate::db::parity_db::GarbageCollectableDb;
use crate::db::{
BlockstoreWriteOpsSubscribable, EthMappingsStore, MemoryDB, PersistentStore, SettingsStore,
SettingsStoreExt,
BLOCK_BLOOM_LEN, BlockstoreWriteOpsSubscribable, EthBlockBloomStore, EthMappingsStore,
MemoryDB, PersistentStore, SettingsStore, SettingsStoreExt,
};
use crate::libp2p_bitswap::BitswapStoreReadWrite;
use crate::prelude::*;
Expand Down Expand Up @@ -315,6 +315,25 @@ impl<WriterT: EthMappingsStore> EthMappingsStore for ManyCar<WriterT> {
}
}

impl<WriterT: EthBlockBloomStore> EthBlockBloomStore for ManyCar<WriterT> {
fn read_bloom(&self, key: &Cid) -> anyhow::Result<Option<[u8; BLOCK_BLOOM_LEN]>> {
EthBlockBloomStore::read_bloom(self.writer(), key)
}

fn write_bloom(
&self,
key: &Cid,
height: ChainEpoch,
bloom: &[u8; BLOCK_BLOOM_LEN],
) -> anyhow::Result<()> {
EthBlockBloomStore::write_bloom(self.writer(), key, height, bloom)
}

fn delete_blooms_before_height(&self, height: ChainEpoch) -> anyhow::Result<()> {
EthBlockBloomStore::delete_blooms_before_height(self.writer(), height)
}
}

impl<T: Blockstore + SettingsStore> super::super::HeaviestTipsetKeyProvider for ManyCar<T> {
fn heaviest_tipset_key(&self) -> anyhow::Result<Option<TipsetKey>> {
match SettingsStoreExt::read_obj::<TipsetKey>(self, crate::db::setting_keys::HEAD_KEY)? {
Expand Down
1 change: 1 addition & 0 deletions src/db/db_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use std::path::PathBuf;
#[derive(Delegate)]
#[delegate(SettingsStore)]
#[delegate(EthMappingsStore)]
#[delegate(EthBlockBloomStore)]
#[delegate(HeaviestTipsetKeyProvider)]
#[delegate(BitswapStoreRead)]
#[delegate(BitswapStoreReadWrite)]
Expand Down
10 changes: 9 additions & 1 deletion src/db/gc/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use crate::cid_collections::FileBackedCidHashSet;
use crate::cli_shared::chain_path;
use crate::db::DbImpl;
use crate::db::{
BlockstoreWriteOpsSubscribable, CAR_DB_DIR_NAME, HeaviestTipsetKeyProvider,
BlockstoreWriteOpsSubscribable, CAR_DB_DIR_NAME, EthBlockBloomStore, HeaviestTipsetKeyProvider,
car::{ForestCar, ReloadableManyCar, forest::new_forest_car_temp_path_in},
db_engine::db_root,
parity_db::GarbageCollectableDb,
Expand Down Expand Up @@ -348,6 +348,14 @@ impl SnapshotGarbageCollector {
.unwrap_or_default()
);

// Prune blooms whose events are no longer retained by the lite snapshot.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll let @hanabi1224 chip in here.

if let Ok(head) = db.heaviest_car_tipset() {
let cutoff = head.epoch() - self.recent_state_roots;
if let Err(e) = db.delete_blooms_before_height(cutoff) {
tracing::warn!("failed to prune stale block blooms: {e:#}");
}
}

// Reset chain head. Note that `self.exported_head_key` is guaranteed to be present,
// see `*self.exported_head_key.write() = Some(head_ts.key().clone());` in `export_snapshot`.
for tsk_opt in [
Expand Down
45 changes: 44 additions & 1 deletion src/db/memory.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright 2019-2026 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

use super::{EthMappingsStore, SettingsStore, SettingsStoreExt};
use super::{
BLOCK_BLOOM_LEN, EthBlockBloomStore, EthMappingsStore, SettingsStore, SettingsStoreExt,
decode_block_bloom, encode_block_bloom,
};
use crate::blocks::{Tipset, TipsetKey};
use crate::db::PersistentStore;
use crate::libp2p_bitswap::{BitswapStoreRead, BitswapStoreReadWrite};
Expand All @@ -21,6 +24,7 @@ pub struct MemoryDB {
settings_db: RwLock<HashMap<String, Vec<u8>>>,
pub eth_mappings_db: RwLock<HashMap<EthHash, Vec<u8>>>,
ts_lookup_db: RwLock<HashMap<ChainEpoch, TipsetKey>>,
eth_block_bloom_db: RwLock<HashMap<Cid, Vec<u8>>>,
}

impl MemoryDB {
Expand Down Expand Up @@ -143,6 +147,35 @@ impl EthMappingsStore for MemoryDB {
}
}

impl EthBlockBloomStore for MemoryDB {
fn read_bloom(&self, key: &Cid) -> anyhow::Result<Option<[u8; BLOCK_BLOOM_LEN]>> {
Ok(self
.eth_block_bloom_db
.read()
.get(key)
.and_then(|entry| decode_block_bloom(entry).map(|(_, bloom)| *bloom)))
}

fn write_bloom(
&self,
key: &Cid,
height: ChainEpoch,
bloom: &[u8; BLOCK_BLOOM_LEN],
) -> anyhow::Result<()> {
self.eth_block_bloom_db
.write()
.insert(*key, encode_block_bloom(height, bloom));
Ok(())
}

fn delete_blooms_before_height(&self, height: ChainEpoch) -> anyhow::Result<()> {
self.eth_block_bloom_db
.write()
.retain(|_, entry| decode_block_bloom(entry).is_some_and(|(h, _)| h >= height));
Ok(())
}
}

impl Blockstore for MemoryDB {
fn get(&self, k: &Cid) -> anyhow::Result<Option<Vec<u8>>> {
Ok(self.blockchain_db.read().get(k).cloned().or(self
Expand Down Expand Up @@ -234,4 +267,14 @@ mod tests {
assert!(car.has(&key1).unwrap());
assert!(car.has(&key2).unwrap());
}

#[test]
fn block_bloom_encode_decode() {
let bloom = [0xab; 256];
let entry = encode_block_bloom(42, &bloom);
let (height, decoded) = decode_block_bloom(&entry).unwrap();
assert_eq!(height, 42);
assert_eq!(decoded, &bloom);
assert!(decode_block_bloom(&[0, 1, 2]).is_none());
}
}
2 changes: 2 additions & 0 deletions src/db/migration/migration_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::Config;
use crate::db::migration::v0_22_1::Migration0_22_0_0_22_1;
use crate::db::migration::v0_26_0::Migration0_25_3_0_26_0;
use crate::db::migration::v0_31_0::Migration0_30_5_0_31_0;
use crate::db::migration::v0_33_8::Migration0_33_7_0_33_8;
use anyhow::Context as _;
use anyhow::bail;
use itertools::Itertools;
Expand Down Expand Up @@ -157,6 +158,7 @@ create_migrations!(
"0.22.0" -> "0.22.1" @ Migration0_22_0_0_22_1,
"0.25.3" -> "0.26.0" @ Migration0_25_3_0_26_0,
"0.30.5" -> "0.31.0" @ Migration0_30_5_0_31_0,
"0.33.7" -> "0.33.8" @ Migration0_33_7_0_33_8,
);

/// Creates a migration chain from `start` to `goal`. The chain is chosen to be the shortest
Expand Down
1 change: 1 addition & 0 deletions src/db/migration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod migration_map;
mod v0_22_1;
mod v0_26_0;
mod v0_31_0;
mod v0_33_8;
mod void_migration;

pub use db_migration::DbMigration;
141 changes: 141 additions & 0 deletions src/db/migration/v0_33_8.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Copyright 2019-2026 ChainSafe Systems
// SPDX-License-Identifier: Apache-2.0, MIT

//! Migration logic for databases with the v0.33.7 schema to v0.33.8.
//! An `EthBlockBloom` column has been added to store per-tipset Ethereum block logs blooms.

use super::migration_map::MigrationOperation;
use crate::Config;
use crate::db::migration::migration_map::MigrationOperationExt as _;
use anyhow::Context;
use semver::Version;
use std::path::{Path, PathBuf};
use tracing::info;

pub(super) struct Migration0_33_7_0_33_8 {
from: Version,
to: Version,
}

/// Migrates the database from version 0.33.7 to 0.33.8
impl MigrationOperation for Migration0_33_7_0_33_8 {
fn new(from: Version, to: Version) -> Self
where
Self: Sized,
{
Self { from, to }
}

fn from(&self) -> &Version {
&self.from
}

fn to(&self) -> &Version {
&self.to
}

fn migrate_core(&self, chain_data_path: &Path, _: &Config) -> anyhow::Result<PathBuf> {
let old_db = self.old_db_path(chain_data_path);
let temp_db = self.temporary_db_path(chain_data_path);

info!(
"Renaming database directory from {} to {}",
old_db.display(),
temp_db.display()
);
std::fs::rename(&old_db, &temp_db).context("failed to rename database directory")?;

info!("Adding EthBlockBloom column to database");
let mut opts = paritydb_0_33_7::to_options(temp_db.clone());
if let Err(e) =
parity_db::Db::add_column(&mut opts, paritydb_0_33_7::eth_block_bloom_column_options())
{
// Restore the original database so a failed migration never strands the only copy in temp.
if let Err(restore) = std::fs::rename(&temp_db, &old_db) {
tracing::error!(
"failed to restore database to {}; data is preserved at {}: {restore}",
old_db.display(),
temp_db.display()
);
}
return Err(e).context("failed to add EthBlockBloom column");
}

// Create a placeholder so the delete step in `migrate` succeeds.
std::fs::create_dir_all(&old_db).context("failed to create placeholder directory")?;

info!("Migration completed successfully");
Ok(temp_db)
}
}

/// Database settings from Forest `v0.33.7`
mod paritydb_0_33_7 {
use parity_db::{ColumnOptions, CompressionType, Options};
use std::path::PathBuf;
use strum::{Display, EnumIter, IntoEnumIterator};

#[derive(Copy, Clone, Debug, PartialEq, EnumIter, Display)]
#[repr(u8)]
pub(super) enum DbColumn {
GraphDagCborBlake2b256,
GraphFull,
Settings,
EthMappings,
PersistentGraph,
}

impl DbColumn {
fn create_column_options(compression: CompressionType) -> Vec<ColumnOptions> {
DbColumn::iter()
.map(|col| match col {
DbColumn::GraphDagCborBlake2b256 | DbColumn::PersistentGraph => ColumnOptions {
preimage: true,
compression,
..Default::default()
},
DbColumn::GraphFull => ColumnOptions {
preimage: true,
btree_index: true,
compression,
..Default::default()
},
DbColumn::Settings => ColumnOptions {
preimage: false,
btree_index: true,
compression,
..Default::default()
},
DbColumn::EthMappings => ColumnOptions {
preimage: false,
btree_index: false,
compression,
..Default::default()
},
})
.collect()
}
}

/// Options for the `EthBlockBloom` column introduced in v0.33.8.
pub(super) fn eth_block_bloom_column_options() -> ColumnOptions {
ColumnOptions {
preimage: false,
btree_index: true,
compression: CompressionType::Lz4,
..Default::default()
}
}

pub(super) fn to_options(path: PathBuf) -> Options {
Options {
path,
sync_wal: true,
sync_data: true,
stats: false,
salt: None,
columns: DbColumn::create_column_options(CompressionType::Lz4),
compression_threshold: [(0, 128)].into_iter().collect(),
}
}
}
Loading
Loading