Skip to content

Add network field to GlobalInfo (GET /api/v1/global)#367

Open
vnprc wants to merge 4 commits intostratum-mining:mainfrom
vnprc:feat/globalinfo-network
Open

Add network field to GlobalInfo (GET /api/v1/global)#367
vnprc wants to merge 4 commits intostratum-mining:mainfrom
vnprc:feat/globalinfo-network

Conversation

@vnprc
Copy link
Copy Markdown

@vnprc vnprc commented Mar 24, 2026

Summary

  • Add network: Option<String> to GlobalInfo so the Bitcoin network is machine-readable from the monitoring REST API without inspecting config files or Prometheus metrics
  • Pool populates it from its [pool] TOML config (network = "regtest")
  • Translator propagates it by polling GET <upstream_monitoring_url>/api/v1/global every 60 seconds in a background task

Motivation

Downstream consumers of the monitoring API (dashboards, alerting tools, and same for translator proxy) need to know which Bitcoin network the pool is operating on. Today this requires reading config files out-of-band. Exposing it in GlobalInfo makes it a first-class, machine-readable API field.

Changes

stratum-apps

  • GlobalInfo.network: Option<String> (serializes to null when not configured)
  • ServerState.network: Arc<RwLock<Option<String>>> for runtime updates
  • MonitoringServer::with_network() — builder method for static configuration
  • MonitoringServer::network_handle() — returns an Arc clone for background tasks that need to update the field after run() consumes self

pool-apps/pool

  • PoolConfig.network: Option<String> with #[serde(default)] (backward-compatible)
  • Pool calls .with_network(config.network()) on startup

miner-apps/translator

  • TranslatorConfig.upstream_monitoring_url: Option<String> with #[serde(default)]
  • On startup, calls monitoring_server.network_handle() then spawns poll_network_from_pool(): fetches pool's /api/v1/global immediately, then every 60 seconds; exits cleanly on cancellation
  • Applied to both the primary startup path and the fallback-restart path
  • reqwest added as a dependency (default-features = false, features = ["json"])

integration-tests

  • start_pool_with_network and start_sv2_translator_with_upstream_monitoring helpers added (existing helpers unchanged, delegate to new ones)
  • global_info_exposes_network test: starts pool with network = "regtest", starts translator pointing at pool's monitoring server, asserts pool exposes "regtest" immediately and translator propagates it within 10 seconds

Network value convention

Values follow bitcoin-cli -getinfo / bitcoin-cli getblockchaininfo convention: "main", "test", "testnet4", "regtest", "signet". The field is null if not configured.

Test plan

  • cargo clippy --manifest-path stratum-apps/Cargo.toml -- -D warnings -A dead-code — clean
  • cargo test --manifest-path stratum-apps/Cargo.toml — 29/29 pass
  • cargo fmt --manifest-path stratum-apps/Cargo.toml -- --check — clean
  • Pool and translator build cleanly with reqwest added
  • global_info_exposes_network integration test passes end-to-end
  • Existing monitoring integration tests unaffected

@Alkamal01
Copy link
Copy Markdown

Alkamal01 commented Apr 7, 2026

Overall this looks clean and well-structured, especially how the changes are split across components and gated behind the monitoring feature.

A few things I think we should address before merging:

Must fix before merge

1. reqwest is always compiled in

Even though poll_network_from_pool is behind #[cfg(feature = "monitoring")], the reqwest dependency itself is still unconditional in translator/Cargo.toml. That means every build pulls in the full HTTP client, even when monitoring is disabled.

It would be better to make this dependency conditional on the monitoring feature so it’s only included when actually needed.

2. Missing TLS support and no URL validation

Right now:

reqwest = { version = "0.12", default-features = false, features = ["json"] }

With default features disabled and no TLS backend enabled (rustls-tls or native-tls), HTTPS URLs will fail at runtime with a pretty opaque error.

The config docs say http://, but nothing enforces that. If someone sets an https:// URL, it’ll just fail silently and be hard to debug.

Two reasonable fixes here:

  • Enable TLS support (e.g. rustls-tls), or
  • Validate the URL scheme at startup and fail fast with a clear error if it’s not http://

3. network value isn’t validated

Right now any string from the TOML config is accepted. That makes it easy for typos like "mainnet" or "testnet" to slip through and propagate downstream.

It would be safer to either:

  • Introduce a BtcNetwork enum, or
  • Validate the value at startup against known options ("main", "test", "testnet4", "regtest", "signet")

Suggestions (non-blocking)

Lock poisoning handling

self.state.network.write().unwrap() will panic without much context if the lock is poisoned. Using .expect("network lock poisoned") would at least make debugging clearer.

Startup retry behavior

If the first fetch fails, the code waits the full 60 seconds before retrying. That means network can stay null for up to a minute if the monitoring server is still coming up. A shorter initial retry interval might improve this.

Integration test timing

The current 10-second wait could be a bit tight on slower CI machines. Bumping it to 20–30 seconds or documenting why 10 is safe would make this more robust.

Test coverage

The test covers the happy path, but it’d be good to also check:

  • behavior when the upstream URL is unreachable
  • that the actual network value is correct, not just that the endpoint responds

Questions

  • What led to choosing reqwest here? For a simple periodic JSON fetch, it feels a bit heavy. Something like ureq (in a dedicated thread) or even a minimal tokio::net::TcpStream implementation could reduce dependencies and make the footprint easier to audit. The size increase in Cargo.lock suggests this might be worth revisiting.
  • Is validation of the network field planned separately, or should it be handled in this PR?
  • Has this been tested end-to-end outside of regtest (e.g. signet or testnet4)?

Verdict

Needs changes.

The unconditional reqwest dependency and the missing TLS/URL handling should be addressed before merging. The network validation is also important, whether fixed here or tracked explicitly as a follow-up.

@vnprc vnprc force-pushed the feat/globalinfo-network branch from fd8c1a3 to fc7e0ce Compare April 14, 2026 16:41
vnprc and others added 4 commits April 14, 2026 13:49
Add `network: Option<String>` to `GlobalInfo` so the Bitcoin network
is machine-readable from `GET /api/v1/global` without consulting
Prometheus or config files.

- `GlobalInfo` gets `pub network: Option<String>`
- `ServerState.network`: `Option<String>` → `Arc<RwLock<Option<String>>>`
- `MonitoringServer::with_network(self, Option<String>)` writes into
  the existing Arc so any handle obtained via `network_handle()` remains
  valid after the call
- `MonitoringServer::network_handle()` returns a clone of the Arc for
  background tasks that need to update network after `run()` consumes self
- `handle_global` reads via `.read().unwrap().clone()`
- Tests: `global_endpoint_with_no_sources` asserts network is null;
  new `global_endpoint_network_field` asserts null and populated cases
Pool infers the Bitcoin network from the sv2-tp port in tp_address using
well-known default ports (8442=mainnet, 18442=testnet3, 48442=testnet4,
38442=signet, 18447=regtest). An explicit `network` config field remains
as an optional override for non-standard port setups.

Translator fetches network from pool's /api/v1/global once per upstream
connection instead of polling every 60 seconds. Retries are handled by
the existing reconnect logic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove reqwest from translator entirely; replace with a plain hyper
  HTTP/1.1 client inside stratum-apps/monitoring. hyper is already
  compiled transitively via axum, so no new Cargo.lock entries are
  introduced.

- MonitoringServer gains with_upstream_monitoring_url() which validates
  the http:// scheme, stores the URL, and spawns a one-shot fetch in
  run(). The old network_handle() method (which leaked internal Arc state
  into application code) is removed.

- Translator chains .with_upstream_monitoring_url() onto the
  MonitoringServer builder; fetch_network_from_pool() private function
  removed from translator/mod.rs.

- Fix network_from_tp_port to use bitcoin-cli / getblockchaininfo names:
  "main" (was "mainnet"), "test" (was "testnet3"). Add VALID_NETWORKS
  constant; effective_network() validates explicit overrides against it.
  Add valid_networks_covers_known_port_outputs unit test.

- Change all RwLock .unwrap() to .expect("network lock poisoned").

- Rename integration test to global_info_network_from_config_override;
  bump polling deadline 10s -> 30s; add
  global_info_network_unreachable_upstream test.
…tum-apps

Move network_from_tp_port() and VALID_NETWORKS from pool-apps (private) into
stratum-apps/src/tp_type.rs as public API. Add BitcoinNetwork::as_network_str()
and TemplateProviderType::infer_network() so any application using
TemplateProviderType can derive the Bitcoin network without duplicating the
port-mapping logic.

Pool config is updated to delegate to infer_network() instead of carrying its
own copy of the helper function.

JobDeclaratorClientConfig gains the same effective_network() / with_network()
pattern as PoolConfig. For Sv2Tp the network is inferred from the sv2-tp port;
for BitcoinCoreIpc it is taken directly from the BitcoinNetwork enum value. An
explicit network override field (serde default) is provided for non-standard
port setups. Both the initial startup and reconnect MonitoringServer paths are
updated to call .with_network(config.effective_network()).

Integration test lib adds start_jdc_with_network_override(); new test
global_info_network_jdc_from_config_override verifies JDC GlobalInfo exposes
the network field. Unit tests added to stratum-apps/tp_type, pool config, and
JDC config.
@vnprc vnprc force-pushed the feat/globalinfo-network branch from dfa8ab3 to 5e3bb6f Compare April 14, 2026 17:50
@vnprc
Copy link
Copy Markdown
Author

vnprc commented Apr 14, 2026

Thanks for the detailed review @Alkamal01!

I pushed three commits today and rebased onto current upstream main (95 commits, no conflicts). I believe this PR is ready for another look.

feat: wire network field through pool, translator, and integration test

  • Pool infers Bitcoin network from sv2-tp port (tp_address in config) using well-known default ports; falls back to an explicit network override for non-standard setups
  • Translator fetches network from pool once per upstream connection via upstream_monitoring_url

fix(monitoring): address PR review feedback

  • network_from_tp_port returns bitcoin-cli names ("main", "test") matching getblockchaininfo; VALID_NETWORKS constant validates explicit overrides
  • reqwest removed from translator; fetch logic moved into MonitoringServer using hyper (already compiled transitively via axum — no new Cargo.lock entries). This is a better design because the monitoring module now owns the logic for querying monitoring endpoints upstream, and no heavyweight dependencies.
  • with_upstream_monitoring_url() replaces the old network_handle() so no internal Arc leaks into application code
  • Integration test renamed to global_info_network_from_config_override; timeout bumped to 30s; new global_info_network_unreachable_upstream test added

feat: extend network inference to JDC; move tp_type utilities to stratum-apps

  • network_from_tp_port, VALID_NETWORKS, BitcoinNetwork::as_network_str(), and TemplateProviderType::infer_network() promoted to public API in stratum-apps/src/tp_type.rs; pool config delegates to infer_network() instead of carrying its own private copy
  • JobDeclaratorClientConfig gains effective_network(), with_network(), and a network override field — same pattern as pool. Sv2Tp uses port inference; BitcoinCoreIpc derives the network directly from its BitcoinNetwork enum value. The override covers non-standard port setups
  • Both the initial startup and reconnect MonitoringServer paths in JDC call .with_network(config.effective_network())
  • New start_jdc_with_network_override integration test helper and global_info_network_jdc_from_config_override test; unit tests added to tp_type, pool config, and JDC config

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants