Skip to content

Commit fce2a2b

Browse files
committed
dash(web): --external-ip override for the dashboard Stratum URL (NAT/port-mapped nodes)
The dashboard Stratum-URL card renders nodeInfo.external_ip. On a node that NATs out through a shared gateway (both hotel DASH nodes: LAN 192.168.1.x behind one gateway), the auto-detected OUTBOUND IP is not the address miners dial -- they reach the external-mapped hosts. Add --external-ip (alias --stratum-advertise / --public-host) to main_dash, feeding the existing MiningInterface::set_external_ip() seam (parity with main_ltc.cpp:1626). rest_node_info() then serves the operator-advertised host verbatim; unset keeps the honest-absent 0.0.0.0 sentinel so the dashboard auto-detect / window.location.hostname fallback is unchanged (no regression). Web layer only, non-consensus. Regression-locks the flag -> served-external_ip plumbing with two KATs (unset honest-absent; operator host served verbatim).
1 parent a437a5c commit fce2a2b

2 files changed

Lines changed: 56 additions & 3 deletions

File tree

src/c2pool/main_dash.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ void print_banner(const char* argv0)
199199
<< " [--listen [HOST:]PORT] [--addnode HOST:PORT]... [--connect HOST:PORT]...\n"
200200
<< " [--stratum [HOST:]PORT] [--coin-p2p-connect HOST:PORT]...\n"
201201
<< " [--web-port PORT] [--web-host ADDR] [--dashboard-dir PATH]\n"
202+
<< " [--external-ip ADDR]\n"
202203
<< " [--embedded-utxo]\n"
203204
<< " [--give-author PCT] [-f|--fee PCT] [--node-owner-address ADDR]\n"
204205
<< " [--redistribute pplns|fee|boost|donate]\n"
@@ -229,6 +230,10 @@ void print_banner(const char* argv0)
229230
<< " are bound to the REAL DASH tracker; local hashrate comes from the\n"
230231
<< " DASH stratum acceptor. If stratum and web ports collide the web\n"
231232
<< " port moves to stratum+1.\n"
233+
<< " --external-ip ADDR (alias --stratum-advertise / --public-host)\n"
234+
<< " overrides the miner-facing host shown in the dashboard Stratum\n"
235+
<< " URL -- for NAT / port-mapped nodes whose outbound IP is not the\n"
236+
<< " address miners dial; unset => auto-detect (no regression).\n"
232237
<< "Consensus: X11 PoW + block identity; 2.5 min spacing; 5 DASH post-V20\n"
233238
<< " base, -1/14 per 210240; masternode payment 3/4 of block value.\n";
234239
}
@@ -395,7 +400,8 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
395400
const std::string& redistribute_mode,
396401
bool no_p2p_relay,
397402
bool embedded_mainnet,
398-
const std::string& coin_zmq_hashblock)
403+
const std::string& coin_zmq_hashblock,
404+
const std::string& external_ip)
399405
{
400406
namespace io = boost::asio;
401407

@@ -568,6 +574,12 @@ int run_node(bool testnet, const std::string& rpc_endpoint,
568574
#endif
569575
mi->set_worker_port(stratum_port); // display only (see divergence note)
570576
mi->set_p2p_port(bind_port);
577+
// Serve the operator-advertised miner-facing host (--external-ip) so
578+
// the dashboard Stratum URL renders the NAT-external address miners
579+
// actually dial, not the auto-detected outbound gateway IP. Unset =>
580+
// MiningInterface keeps its auto-detect path (no regression).
581+
if (!external_ip.empty())
582+
mi->set_external_ip(external_ip);
571583
// p2pool-compat protocol_version for /local_stats. The core seam
572584
// documents this exact value for DASH (web_server.hpp:565) and it
573585
// matches the sharechain SSOT floor.
@@ -2267,6 +2279,9 @@ int main(int argc, char** argv)
22672279
std::string dashboard_dir = "web-static"; // --dashboard-dir static asset root
22682280
std::string redistribute_mode = "pplns"; // --redistribute pplns|fee|boost|donate
22692281
std::string coin_zmq_hashblock; // --coin-zmq-hashblock ENDPOINT (opt-in dashd ZMQ hashblock instant tip-notify, e.g. tcp://127.0.0.1:28332)
2282+
// Operator-supplied miner-facing host for the dashboard Stratum URL.
2283+
// Empty => auto-detect the outbound public IP (current behaviour).
2284+
std::string external_ip; // --external-ip / --stratum-advertise / --public-host
22702285
for (int i = 1; i < argc; ++i) {
22712286
if (std::strcmp(argv[i], "--version") == 0) {
22722287
std::cout << "c2pool-dash " << C2POOL_VERSION << "\n";
@@ -2337,6 +2352,14 @@ int main(int argc, char** argv)
23372352
}
23382353
else if (std::strcmp(argv[i], "--web-host") == 0 && i + 1 < argc)
23392354
web_host = argv[++i];
2355+
// Miner-facing host override for the dashboard Stratum URL. Both hotel
2356+
// nodes NAT out through one gateway, so the auto-detected outbound IP
2357+
// is NOT the address miners reach; the operator advertises the real
2358+
// external-mapped host here. Aliases match how the flag is referenced.
2359+
else if ((std::strcmp(argv[i], "--external-ip") == 0 ||
2360+
std::strcmp(argv[i], "--stratum-advertise") == 0 ||
2361+
std::strcmp(argv[i], "--public-host") == 0) && i + 1 < argc)
2362+
external_ip = argv[++i];
23402363
else if (std::strcmp(argv[i], "--dashboard-dir") == 0 && i + 1 < argc)
23412364
dashboard_dir = argv[++i];
23422365
else if (std::strcmp(argv[i], "--stratum") == 0 && i + 1 < argc) {
@@ -2418,7 +2441,8 @@ int main(int argc, char** argv)
24182441
embedded_utxo, dev_donation, node_owner_fee,
24192442
node_owner_address, redistribute_mode, no_p2p_relay,
24202443
embedded_mainnet,
2421-
coin_zmq_hashblock);
2444+
coin_zmq_hashblock,
2445+
external_ip);
24222446
}
24232447
return run_selftest();
24242448
}

test/test_web_honesty_regression.cpp

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -574,4 +574,33 @@ TEST(WebHonestyRegression, PatronSendmanySelfLabelsAsStubNeverFabricatesPayouts)
574574
<< "never label-as-stub while surfacing payout splits -- silent lie";
575575
EXPECT_EQ(p.value("total", std::string{}), "12.5")
576576
<< "the echoed total must be the operator-supplied value, not invented";
577-
}
577+
}
578+
// ── Stratum-URL external_ip override (DASH NAT/port-mapped nodes) ──────────
579+
// The dashboard Stratum-URL card renders nodeInfo.external_ip
580+
// (dashboard.html:2889-2892). When a node NATs out through a shared gateway
581+
// (both hotel DASH nodes: LAN 192.168.1.x, one public 31.172.65.125), the
582+
// auto-detected OUTBOUND IP is NOT the address miners dial -- they reach the
583+
// external-mapped hosts (109.161.57.3 / 109.161.52.148). c2pool-dash exposes
584+
// --external-ip (alias --stratum-advertise / --public-host) which feeds
585+
// set_external_ip(); rest_node_info() must then SERVE that operator-supplied
586+
// host verbatim so the Stratum URL is truthful. Unset must stay honest-absent
587+
// ("0.0.0.0"), leaving the auto-detect / window.location.hostname fallback --
588+
// no regression. Pins the flag -> served-external_ip plumbing.
589+
TEST(WebHonestyRegression, NodeInfoExternalIpUnsetIsHonestlyUnspecified) {
590+
MiningInterface mi(/*testnet=*/true, /*node=*/nullptr, Blockchain::DASH);
591+
json ni = mi.rest_node_info();
592+
EXPECT_EQ(ni.value("external_ip", std::string{}), "0.0.0.0")
593+
<< "unset external_ip must serve the honest-absent sentinel so the "
594+
"dashboard falls back to auto-detect / window.location.hostname";
595+
}
596+
597+
TEST(WebHonestyRegression, NodeInfoExternalIpServesOperatorAdvertisedHost) {
598+
MiningInterface mi(/*testnet=*/true, /*node=*/nullptr, Blockchain::DASH);
599+
// Operator advertises the real miner-facing external-mapped host (primary
600+
// hotel node), NOT the auto-detected 31.172.65.125 NAT gateway.
601+
mi.set_external_ip("109.161.57.3");
602+
json ni = mi.rest_node_info();
603+
EXPECT_EQ(ni.value("external_ip", std::string{}), "109.161.57.3")
604+
<< "served external_ip must be the operator-advertised miner-facing "
605+
"host so the dashboard Stratum URL is not the wrong NAT IP";
606+
}

0 commit comments

Comments
 (0)