feat(indexer)!: make storage location caller-driven, not config-driven
The indexer's storage location was the `home` field of IndexerConfig, used only to derive the RocksDB dir. Defaulting to "." meant it landed in the process CWD — fine for the standalone service, but wrong when the indexer runs embedded in a logos_host subprocess (RocksDB ended up in an arbitrary/unwritable dir). Storage location is an operational concern the host should own, not something baked into a user-editable config.
Remove `home` from IndexerConfig and pass the storage directory explicitly:
- core: `IndexerCore::new(config, storage_dir)` derives `<storage_dir>/rocksdb`.
- ffi: `start_indexer(runtime, config_path, storage_dir)`; null/empty storage_dir falls back to ".". Lets a host (e.g. a Logos module's instance persistence path) own where state lives.
- service: `run_server(config, storage_dir, port)` + a `--data-dir` flag (default ".") on the binary, preserving current behaviour.
- drop `home` from the committed indexer config JSONs and the test fixtures.
BREAKING CHANGE: `start_indexer` gains a `storage_dir` parameter and IndexerConfig no longer has a `home` field.
2026-06-19 23:18:41 +03:00
|
|
|
use std::{fs::File, io::BufReader, path::Path, time::Duration};
|
2026-01-23 10:39:34 +02:00
|
|
|
|
2026-01-29 22:20:42 +03:00
|
|
|
use anyhow::{Context as _, Result};
|
2026-03-13 18:23:39 +02:00
|
|
|
use common::config::BasicAuth;
|
2026-06-23 14:21:16 +02:00
|
|
|
use cross_zone_inbox_core::CrossZoneConfig;
|
2026-02-24 20:52:14 +03:00
|
|
|
use humantime_serde;
|
2026-06-24 11:24:28 +02:00
|
|
|
use lee::AccountId;
|
2026-01-29 22:20:42 +03:00
|
|
|
pub use logos_blockchain_core::mantle::ops::channel::ChannelId;
|
2026-01-13 15:11:51 +02:00
|
|
|
use serde::{Deserialize, Serialize};
|
2026-01-27 09:46:31 +02:00
|
|
|
use url::Url;
|
2026-01-12 15:51:24 +02:00
|
|
|
|
2026-01-22 14:44:48 +02:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
2026-02-03 11:36:07 +02:00
|
|
|
pub struct ClientConfig {
|
2026-01-27 09:46:31 +02:00
|
|
|
pub addr: Url,
|
2026-02-10 00:19:37 +03:00
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
2026-01-27 09:46:31 +02:00
|
|
|
pub auth: Option<BasicAuth>,
|
2026-01-22 14:44:48 +02:00
|
|
|
}
|
|
|
|
|
|
2026-01-13 15:11:51 +02:00
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
2026-01-12 15:51:24 +02:00
|
|
|
pub struct IndexerConfig {
|
2026-02-24 20:52:14 +03:00
|
|
|
#[serde(with = "humantime_serde")]
|
|
|
|
|
pub consensus_info_polling_interval: Duration,
|
2026-04-29 14:05:23 +02:00
|
|
|
pub bedrock_config: ClientConfig,
|
2026-01-22 14:44:48 +02:00
|
|
|
pub channel_id: ChannelId,
|
2026-06-23 14:21:16 +02:00
|
|
|
/// Cross-zone configuration. `None` disables the indexer's cross-zone handling.
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub cross_zone: Option<CrossZoneConfig>,
|
2026-06-24 11:24:28 +02:00
|
|
|
/// Bridge-lock holdings to seed into genesis, mirroring the sequencer's
|
|
|
|
|
/// `SupplyBridgeLockHolding` actions. They are not produced by any
|
|
|
|
|
/// transaction, so the indexer must seed them to match the sequencer's state.
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub bridge_lock_holdings: Vec<BridgeLockHolding>,
|
2026-06-30 13:58:37 +03:00
|
|
|
/// Whether to wipe the indexer store and re-index from scratch when the startup
|
|
|
|
|
/// chain-identity check finds the channel serving a different block than the one
|
|
|
|
|
/// stored at the same id.
|
2026-06-26 20:37:35 +03:00
|
|
|
///
|
|
|
|
|
/// Defaults to `false`: on mismatch the indexer refuses to start.
|
|
|
|
|
#[serde(default)]
|
|
|
|
|
pub allow_chain_reset: bool,
|
2026-06-24 11:24:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// A genesis-funded bridge-lock holder balance, configured identically on the
|
|
|
|
|
/// sequencer (via `SupplyBridgeLockHolding`) and the indexer.
|
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
|
|
|
pub struct BridgeLockHolding {
|
|
|
|
|
pub holder: AccountId,
|
|
|
|
|
pub amount: u128,
|
2026-01-12 15:51:24 +02:00
|
|
|
}
|
2026-01-23 10:39:34 +02:00
|
|
|
|
|
|
|
|
impl IndexerConfig {
|
2026-03-09 18:27:56 +03:00
|
|
|
pub fn from_path(config_path: &Path) -> Result<Self> {
|
2026-03-03 23:21:08 +03:00
|
|
|
let file = File::open(config_path).with_context(|| {
|
|
|
|
|
format!("Failed to open indexer config at {}", config_path.display())
|
|
|
|
|
})?;
|
2026-01-23 10:39:34 +02:00
|
|
|
let reader = BufReader::new(file);
|
|
|
|
|
|
2026-03-03 23:21:08 +03:00
|
|
|
serde_json::from_reader(reader).with_context(|| {
|
|
|
|
|
format!(
|
|
|
|
|
"Failed to parse indexer config at {}",
|
|
|
|
|
config_path.display()
|
|
|
|
|
)
|
|
|
|
|
})
|
2026-01-23 10:39:34 +02:00
|
|
|
}
|
|
|
|
|
}
|