1use base64::{Engine, prelude::BASE64_STANDARD};
2use eyre::Context as _;
3use jiff::SignedDuration;
4use reth_cli_commands::download::DownloadDefaults;
5use reth_ethereum::node::core::args::{
6 DefaultDiscoveryArgs, DefaultEngineValues, DefaultNetworkArgs, DefaultPayloadBuilderValues,
7 DefaultTraceValues, DefaultTxPoolValues,
8};
9use std::{borrow::Cow, str::FromStr, time::Duration};
10use tempo_chainspec::spec::TEMPO_T7_BASE_FEE_FLOOR;
11use url::Url;
12
13pub(crate) const DEFAULT_DOWNLOAD_URL: &str = "https://snapshots.tempoxyz.dev/4217";
14const SNAPSHOT_API_URL: &str = "https://snapshots.tempoxyz.dev/api/snapshots";
15
16const DEFAULT_LOGS_OTLP_FILTER: &str = "debug";
18
19#[derive(Debug, Clone, clap::Args)]
21pub(crate) struct TelemetryArgs {
22 #[arg(
27 long,
28 value_name = "URL",
29 conflicts_with = "logs_otlp",
30 env = "TEMPO_TELEMETRY_URL"
31 )]
32 pub(crate) telemetry_url: Option<UrlWithAuth>,
33
34 #[arg(long, default_value = "10s")]
36 pub(crate) telemetry_metrics_interval: SignedDuration,
37}
38
39impl TelemetryArgs {
40 pub(crate) fn try_to_config(&self) -> eyre::Result<Option<TelemetryConfig>> {
44 let Some(telemetry_url) = self.telemetry_url.clone().map(Url::from) else {
45 return Ok(None);
46 };
47
48 let username = telemetry_url.username();
50 let password = telemetry_url.password().expect("ensured when parsing args");
51
52 let credentials = format!("{username}:{password}");
54 let encoded = BASE64_STANDARD.encode(credentials.as_bytes());
55 let auth_header = format!("Basic {encoded}");
56
57 if std::env::var_os("OTEL_EXPORTER_OTLP_HEADERS").is_none() {
59 let header_value = format!("Authorization={auth_header}");
60 unsafe {
62 std::env::set_var("OTEL_EXPORTER_OTLP_HEADERS", header_value);
63 }
64 }
65
66 let mut base_url_no_creds = telemetry_url.clone();
68 base_url_no_creds.set_username("").ok();
69 base_url_no_creds.set_password(None).ok();
70
71 let logs_otlp_url = base_url_no_creds
73 .join("opentelemetry/v1/logs")
74 .wrap_err("failed to construct logs OTLP URL")?;
75
76 let metrics_prometheus_url = base_url_no_creds
78 .join("api/v1/import/prometheus")
79 .wrap_err("failed to construct metrics URL")?;
80
81 Ok(Some(TelemetryConfig {
82 logs_otlp_url,
83 logs_otlp_filter: DEFAULT_LOGS_OTLP_FILTER.to_string(),
84 metrics_prometheus_url,
85 metrics_prometheus_interval: self.telemetry_metrics_interval,
86 metrics_auth_header: Some(auth_header),
87 }))
88 }
89}
90
91#[derive(Clone)]
95pub(crate) struct UrlWithAuth(Url);
96
97impl UrlWithAuth {
98 fn redacted(&self) -> Url {
100 let mut url = self.0.clone();
101 url.set_password(Some("***")).ok();
102 url
103 }
104}
105
106impl std::fmt::Debug for UrlWithAuth {
107 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108 write!(f, "{}", self.redacted())
109 }
110}
111
112impl std::fmt::Display for UrlWithAuth {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 write!(f, "{}", self.redacted())
115 }
116}
117
118impl FromStr for UrlWithAuth {
119 type Err = Box<dyn std::error::Error + Send + Sync + 'static>;
120
121 fn from_str(s: &str) -> Result<Self, Self::Err> {
122 let url = s.parse::<Url>()?;
123 if url.username().is_empty() || url.password().is_none() {
124 return Err("URL must include username and password".into());
125 }
126 Ok(Self(url))
127 }
128}
129impl From<UrlWithAuth> for Url {
130 fn from(value: UrlWithAuth) -> Self {
131 value.0
132 }
133}
134
135#[derive(Debug, Clone)]
137pub(crate) struct TelemetryConfig {
138 pub(crate) logs_otlp_url: Url,
140 pub(crate) logs_otlp_filter: String,
142 pub(crate) metrics_prometheus_url: Url,
145 pub(crate) metrics_prometheus_interval: SignedDuration,
147 pub(crate) metrics_auth_header: Option<String>,
149}
150
151fn init_download_urls() {
152 let download_defaults = DownloadDefaults {
153 available_snapshots: vec![
154 Cow::Owned(format!("{DEFAULT_DOWNLOAD_URL} (mainnet)")),
155 Cow::Borrowed("https://snapshots.tempoxyz.dev/42431 (moderato)"),
156 Cow::Borrowed("https://snapshots.tempoxyz.dev/42429 (andantino)"),
157 ],
158 default_base_url: Cow::Borrowed(DEFAULT_DOWNLOAD_URL),
159 default_chain_aware_base_url: None,
160 snapshot_api_url: Cow::Borrowed(SNAPSHOT_API_URL),
161 long_help: None,
162 };
163
164 download_defaults
165 .try_init()
166 .expect("failed to initialize download URLs");
167}
168
169fn init_payload_builder_defaults() {
170 DefaultPayloadBuilderValues::default()
171 .with_interval(Duration::from_millis(100))
172 .with_max_payload_tasks(1)
173 .with_deadline(4)
174 .try_init()
175 .expect("failed to initialize payload builder defaults");
176}
177
178fn init_txpool_defaults() {
179 DefaultTxPoolValues::default()
180 .with_pending_max_count(50000)
181 .with_basefee_max_count(50000)
182 .with_queued_max_count(50000)
183 .with_pending_max_size(100)
184 .with_basefee_max_size(100)
185 .with_queued_max_size(100)
186 .with_no_locals(true)
187 .with_max_queued_lifetime(Duration::from_secs(120))
188 .with_max_new_pending_txs_notifications(150000)
189 .with_max_account_slots(150000)
190 .with_pending_tx_listener_buffer_size(50000)
191 .with_new_tx_listener_buffer_size(50000)
192 .with_disable_transactions_backup(true)
193 .with_additional_validation_tasks(8)
194 .with_minimal_protocol_basefee(TEMPO_T7_BASE_FEE_FLOOR)
195 .with_minimum_priority_fee(Some(0))
196 .with_max_batch_size(50000)
197 .try_init()
198 .expect("failed to initialize txpool defaults");
199}
200
201fn init_engine_defaults() {
202 DefaultEngineValues::default()
203 .with_always_process_payload_attributes_on_canonical_head(true)
212 .with_suppress_persistence_during_build(true)
214 .with_share_sparse_trie_with_payload_builder(true)
215 .with_share_execution_cache_with_payload_builder(true)
216 .try_init()
217 .expect("failed to initialize engine defaults");
218}
219
220fn init_trace_defaults() {
221 DefaultTraceValues::default()
222 .with_service_name("tempo")
223 .with_service_version(env!("CARGO_PKG_VERSION"))
224 .try_init()
225 .expect("failed to initialize trace defaults");
226}
227
228fn init_otlp_defaults() {
229 if std::env::var_os("OTEL_BSP_MAX_QUEUE_SIZE").is_none() {
232 unsafe {
234 std::env::set_var("OTEL_BSP_MAX_QUEUE_SIZE", "65536");
235 }
236 }
237 if std::env::var_os("OTEL_BLRP_MAX_QUEUE_SIZE").is_none() {
238 unsafe {
240 std::env::set_var("OTEL_BLRP_MAX_QUEUE_SIZE", "65536");
241 }
242 }
243}
244
245fn init_network_defaults() {
246 DefaultNetworkArgs::default()
247 .with_enforce_enr_fork_id(true)
248 .try_init()
249 .expect("failed to initialize network defaults");
250}
251
252fn init_discovery_defaults() {
253 DefaultDiscoveryArgs::default()
254 .with_discv5_port(None)
256 .with_discv5_port_ipv6(None)
257 .try_init()
258 .expect("failed to initialize discovery defaults");
259}
260
261pub(crate) fn init_defaults() {
262 init_download_urls();
263 init_payload_builder_defaults();
264 init_txpool_defaults();
265 init_engine_defaults();
266 init_trace_defaults();
267 init_otlp_defaults();
268 init_network_defaults();
269 init_discovery_defaults();
270}