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

@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

@GitGab19
Copy link
Copy Markdown
Member

Can you please clarify the need for these changes?

Could you give a concrete example of when this is needed?

@vnprc
Copy link
Copy Markdown
Author

vnprc commented Apr 15, 2026

Sv2 today does not provide a way to determine what blockchain you are mining on. This is the most basic piece of information I can think of, so that's where I started contributing to sv2-apps.

My mining dashboard displays this field and is running this code now (in my vendored sv2-apps repo, as you can see in this commit).

image

For the first iteration I just displayed a config file setting, but this is just a lie; it can and will be misconfigured eventually. I'm trying to build the plumbing to get this information from the source of truth: the template provider.

This iteration uses standard ports (thanks for the tip @Sjors!) and falls back to displaying a config setting. This is better because it pulls info from the source of truth (the tp) as long as you stick to standard ports. It's still a lie if you deviate from the standard ports, but at least this PR adds the plumbing that routes this information to the monitoring API.

The complete solution is to include this information in the template distribution protocol handshake message. I have opened an issue in the protocol repo here: stratum-mining/sv2-spec#190

Frankly, I am amazed that no one has considered this a problem before. It's not even in the protocol! We have so much work to do...

@vnprc
Copy link
Copy Markdown
Author

vnprc commented Apr 16, 2026

@GitGab19 should i just write the spec and then open a new PR implementing it?

@GitGab19
Copy link
Copy Markdown
Member

I'm not sure this is a problem tbh, but I have to think more about this.

When do you switch networks in production? Which is the specific use case?

@vnprc
Copy link
Copy Markdown
Author

vnprc commented Apr 16, 2026

I switch networks between dev and prod environments. Dev runs regtest and prod runs testnet4.

My goal is to build pool software that runs on autopilot. We will never decentralize block producers if running a pool remains a specialized skillset only available to a select few. If a pleb miner is running hashpool I want to prevent them from missing out on a block because they didn't read the manual. When the SRI install base reaches the scale we need it to reach to decentralize bitcoin block production, the probability of a misconfiguration causing an invalid block approaches 1.

Remember when Marathon mined an invalid block? I do. The objective of this protocol change is to prevent this footgun in an automated fashion.

image

@GitGab19
Copy link
Copy Markdown
Member

I'm not getting your point yet.

If you're running a Pool server (or a JDC) which gets block templates directly from Bitcoin Core (through IPC) or from a Template Provider, it will get templates related to the network where the node is running.

How can this flow ever lead to the error Mara experienced in your example?

@vnprc
Copy link
Copy Markdown
Author

vnprc commented Apr 16, 2026

How does the node network value get displayed to the end user?

edit: How does your grandma know what network she is mining on? She's only computer literate enough to install a "pool-in-a-box" solution and click thru the install wizard. Is she supposed to grep the bitcoind logs? I think we can do better than that.

I guess a more specific answer is this: grandma clicks testnet on the bitcoin node install option and then she clicks mainnet on the pool software option. What happens? It sounds like today the pool software just blindly trusts the node. Why?

It's just not that hard to make sure that this information flows through the system to the end user UI display. Why would we not build this capability? Don't trust verify.

@vnprc
Copy link
Copy Markdown
Author

vnprc commented Apr 16, 2026

How about a different example. The user is playing with the software and switches the node to testnet. They forget to update the SRI config file to also say testnet. Their mining dashboard says mainnet because they use non-standard ports and just entered "mainnet" in the SRI config file somewhere. The dashboard software just blindly presents this config value on the website but they are, in fact, mining on testnet. The user finishes testing their software and neglects to point the node back to mainnet. Now the UI says mainnet, but they are actually mining on testnet. How long before they realize their mistake?

It's just a basic software engineering principle to have a single source of truth. Violating this principle leads to bugs. We violate this principle for the most fundamental piece of data in the whole mining stack: what blockchain are we mining on? The fix is simple. So let's fix it.

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