Skip to content
This repository was archived by the owner on Jan 27, 2026. It is now read-only.

Commit d34fbc8

Browse files
committed
all chains use the same config loader
1 parent 12f66f0 commit d34fbc8

27 files changed

Lines changed: 144 additions & 157 deletions

chain/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,9 @@ async-trait = "0.1.81"
2222
redb = "3.0.2"
2323
serde = { version = "1.0.219", features = ["derive"] }
2424
tokio = { version = "1.45.1", features = ["full", "tracing"] }
25+
tokio-util = "0.7.16"
2526
tower-http = { version = "0.6.6", features = ["cors"] }
2627
tokio-stream = { version = "0.1.17", features = ["sync"] }
2728
thiserror = "2.0.14"
28-
sysinfo = "0.37.0"
29+
sysinfo = "0.37.0"
30+
dotenv = "0.15.0"

chain/src/api.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use std::pin::Pin;
88
use std::sync::Arc;
99
use redb::{Durability, StorageError};
1010
use tokio::task::JoinError;
11+
use tokio_util::sync::CancellationToken;
1112
use redbit::storage::table_writer_api::TaskResult;
1213

1314
#[derive(Debug, thiserror::Error)]
@@ -84,5 +85,10 @@ pub trait BlockProvider<FB: SizeLike, TB: BlockLike>: Send + Sync {
8485
fn block_processor(&self) -> Arc<dyn Fn(&FB) -> Result<TB, ChainError> + Send + Sync>;
8586
fn get_processed_block(&self, hash: <TB::Header as BlockHeaderLike>::Hash) -> Result<Option<TB>, ChainError>;
8687
async fn get_chain_tip(&self) -> Result<TB::Header, ChainError>;
87-
fn stream(&self, remote_chain_tip_header: TB::Header, last_persisted_header: Option<TB::Header>, durability: Durability) -> Pin<Box<dyn Stream<Item = FB> + Send + 'static>>;
88+
fn stream(
89+
&self,
90+
remote_chain_tip_header: TB::Header,
91+
last_persisted_header: Option<TB::Header>,
92+
durability: Durability
93+
) -> (Pin<Box<dyn Stream<Item = FB> + Send + 'static>>, CancellationToken);
8894
}

chain/src/chain_config.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
use config::{Config, ConfigError, Environment, File};
2+
use dotenv::dotenv;
3+
use serde::de::DeserializeOwned;
4+
use std::fmt::Debug;
5+
use std::sync::Once;
6+
use redbit::info;
7+
8+
static DOTENV_ONCE: Once = Once::new();
9+
10+
fn ensure_dotenv_loaded() {
11+
DOTENV_ONCE.call_once(|| {
12+
match dotenv() {
13+
Ok(_) => info!("Config loaded including .env file."),
14+
Err(_) => info!("Config loaded without .env file."),
15+
}
16+
});
17+
}
18+
19+
pub fn load_config<T>(path: &str, prefix: &str) -> Result<T, ConfigError>
20+
where
21+
T: DeserializeOwned + Debug,
22+
{
23+
ensure_dotenv_loaded();
24+
25+
let builder = Config::builder()
26+
.add_source(File::with_name(path).required(true))
27+
.add_source(
28+
Environment::with_prefix(prefix)
29+
.try_parsing(true)
30+
.separator("__"),
31+
);
32+
33+
let cfg = builder.build()?.try_deserialize::<T>()?;
34+
info!("{:#?}", cfg);
35+
Ok(cfg)
36+
}

chain/src/launcher.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::api::{BlockChainLike, BlockLike, BlockProvider, SizeLike};
22
use crate::scheduler::Scheduler;
33
use crate::settings::{AppConfig, DbCacheSize, HttpSettings, IndexerSettings};
4-
use crate::{combine, ChainError};
4+
use crate::{chain_config, combine, ChainError};
55
use futures::future::ready;
66
use redbit::storage::init::{Storage, StorageOwner};
77
use redbit::{error, info, serve, AppError, OpenApiRouter, RequestState, WriteTxContext};
@@ -124,7 +124,7 @@ pub async fn launch<FB: SizeLike + 'static, TB: BlockLike + 'static, CTX: WriteT
124124
where
125125
F: FnOnce(Arc<Storage>) -> Arc<dyn BlockChainLike<TB, CTX>>,
126126
{
127-
let config = AppConfig::new("config/settings").expect("Failed to load app config");
127+
let config: AppConfig = chain_config::load_config("config/settings", "REDBIT").expect("Failed to load Redbit settings");
128128
maybe_console_init();
129129
let (created, storage_owner, storage_view) = build_storage(&config).await?;
130130
let chain: Arc<dyn BlockChainLike<TB, CTX>> = build_chain(Arc::clone(&storage_view));

chain/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,6 @@ pub mod launcher;
88
pub mod task;
99
pub mod batcher;
1010
pub mod stats;
11+
pub mod chain_config;
1112

1213
pub use api::{BlockHeaderLike, SizeLike, BlockLike, BlockChainLike, ChainError};

chain/src/settings.rs

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
1-
use config::{Config, ConfigError, Environment, File};
21
use serde::Deserialize;
32
use std::fmt::Debug;
43
use std::net::SocketAddr;
54
use std::str::FromStr;
65
use std::time::Duration;
76
use sysinfo::System;
8-
use redbit::info;
97

108
#[derive(Clone, Copy)]
119
pub struct Parallelism(pub usize);
@@ -145,15 +143,3 @@ pub struct HttpSettings {
145143
pub enable: bool,
146144
pub bind_address: SocketAddr,
147145
}
148-
149-
impl AppConfig {
150-
pub fn new(path: &str) -> Result<Self, ConfigError> {
151-
let builder =
152-
Config::builder()
153-
.add_source(File::with_name(path).required(true))
154-
.add_source(Environment::with_prefix("REDBIT").try_parsing(true).separator("__"));
155-
let config = builder.build()?.try_deserialize::<AppConfig>()?;
156-
info!("{:#?}", config);
157-
Ok(config)
158-
}
159-
}

chain/src/syncer.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,15 @@ impl<FB: SizeLike + 'static, TB: BlockLike + 'static, CTX: WriteTxContext + 'sta
6666
let block_provider = Arc::clone(&self.block_provider);
6767
let shutdown = shutdown.clone();
6868
task::spawn_named("fetch", async move {
69-
let mut s = block_provider.stream(node_chain_tip_header.clone(), last_persisted_header, default_durability);
69+
let (mut s, cancel_token) = block_provider.stream(node_chain_tip_header.clone(), last_persisted_header, default_durability);
7070
let mut buf: Vec<FB> = Vec::new();
7171
let mut buf_bytes: usize = 0;
7272
loop {
7373
tokio::select! {
7474
biased;
7575
_ = combine::await_shutdown(shutdown.clone()) => {
7676
info!("fetch: shutdown");
77+
cancel_token.cancel();
7778
break;
7879
}
7980
item = s.next() => {
@@ -152,7 +153,7 @@ impl<FB: SizeLike + 'static, TB: BlockLike + 'static, CTX: WriteTxContext + 'sta
152153
}
153154
}
154155
Err(e) => {
155-
error!("processing failure: {e}");
156+
warn!("Processing warning: {e}");
156157
}
157158
}
158159
}

chains/btc/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ redbit = { path = "../../redbit" }
3636
chain = { path = "../../chain" }
3737
tokio = { version = "1.45.1", features = ["full", "tracing"] }
3838
tokio-stream = { version = "0.1.17", features = ["sync"] }
39+
tokio-util = "0.7.16"
3940
config = "0.15.11"
4041
bitcoin = "0.32.0"
4142
bitcoin_hashes = "0.16.0"
@@ -46,7 +47,6 @@ bech32 = "0.11.0"
4647
bs58 = { version = "0.5", features = ["check"] }
4748
axum = { version = "0.8.4", features = ["default", "macros"] }
4849
thiserror = "2.0.12"
49-
dotenv = "0.15.0"
5050
chrono = "0.4.38"
5151
anyhow = "1.0.80"
5252
bincode = "2.0.1"

chains/btc/src/block_provider.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::codec::{TAG_NON_ADDR, TAG_OP_RETURN, TAG_SEGWIT};
2-
use crate::config::BitcoinConfig;
32
use crate::model_v1::{Address, Block, BlockHash, BlockPointer, Header, Height, InputRef, MerkleRoot, ScriptHash, Timestamp, Transaction, TransactionPointer, TxHash, Utxo, Weight};
43
use crate::rest_client::{BtcCBOR, BtcClient};
5-
use crate::ExplorerError;
4+
use crate::{BitcoinConfig, ExplorerError};
65
use async_trait::async_trait;
6+
use tokio_util::sync::CancellationToken;
77
use chain::api::{BlockProvider, ChainError};
88
use chain::monitor::BoxWeight;
99
use chain::settings::Parallelism;
@@ -13,6 +13,7 @@ use redbit::info;
1313
use redbit::*;
1414
use serde_json;
1515
use std::{fs, pin::Pin, sync::Arc};
16+
use chain::chain_config;
1617
use redbit::redb::Durability;
1718

1819
pub struct BtcBlockProvider {
@@ -22,7 +23,7 @@ pub struct BtcBlockProvider {
2223

2324
impl BtcBlockProvider {
2425
pub fn new() -> Result<Arc<Self>, ExplorerError> {
25-
let config = BitcoinConfig::new("config/btc").expect("Failed to load Bitcoin configuration");
26+
let config: BitcoinConfig = chain_config::load_config("config/btc", "BITCOIN").expect("Failed to load Bitcoin configuration");
2627
let client = Arc::new(BtcClient::new(&config)?);
2728
let fetching_par: Parallelism = config.fetching_parallelism.clone();
2829
Ok(Arc::new(BtcBlockProvider { client, fetching_par }))
@@ -193,7 +194,7 @@ impl BlockProvider<BtcCBOR, Block> for BtcBlockProvider {
193194
remote_chain_tip_header: Header,
194195
last_persisted_header: Option<Header>,
195196
durability: Durability
196-
) -> Pin<Box<dyn Stream<Item = BtcCBOR> + Send + 'static>> {
197+
) -> (Pin<Box<dyn Stream<Item = BtcCBOR> + Send + 'static>>, CancellationToken) {
197198
let height_to_index_from = last_persisted_header.map_or(1, |h| h.height.0 + 1);
198199
let heights = height_to_index_from..=remote_chain_tip_header.height.0;
199200
let client = Arc::clone(&self.client);
@@ -204,11 +205,12 @@ impl BlockProvider<BtcCBOR, Block> for BtcBlockProvider {
204205
client.get_block_by_height(Height(height)).await.expect("Failed to fetch block by height")
205206
}
206207
});
207-
match durability {
208+
let stream = match durability {
208209
Durability::None => s.buffer_unordered(self.fetching_par.0).boxed(),
209210
Durability::Immediate => s.buffered(self.fetching_par.0).boxed(),
210211
_ => unreachable!("Only None and Immediate durability modes are supported"),
211-
}
212+
};
213+
(stream, CancellationToken::new())
212214
}
213215
}
214216

chains/btc/src/config.rs

Lines changed: 0 additions & 27 deletions
This file was deleted.

0 commit comments

Comments
 (0)