diff options
| author | Victorio Nascimento <155357371+Vic-Nas@users.noreply.github.com> | 2026-07-27 16:45:28 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-27 17:45:28 -0300 |
| commit | 6ca1a1d6799474f8ca977b93ab630af7ace879c2 (patch) | |
| tree | a0312ebc10fc6a56742d0e5caa85c31eb6d3eaa7 /src/net.cpp | |
| parent | 56df9830d5a002ff9ce3fac47e497673f8159e39 (diff) | |
Add Linux GUI installer via Wine (#3)
* Add Linux GUI installer via Wine
* Fix launcher creation and add BTF_RELAY env var support
* Pool server, Linux GUI, unified binary, README update
* Root Makefile: make linux / make windows, cleaner README
* Remove headless.cpp
* Fix XPM color count mismatch causing toolbar image errors on Linux
* XPM icons: same code on Windows and Linux, no ifdefs around image loading
* Fix GTK icon crash and Linux printf format
* btfrv: 10s socket timeout on relay connects
* Faster peer discovery: more dials per pass, shorter backoff and sleep
* Parallel btf peer connections
* Fix GetMyExternalIP - was disabled, now uses modern IP echo services
* Fix tunnel disconnect on idle and restore external IP detection
* Filter stale Nostr descriptors
* Sort peers by freshness, fresh first
* Pool: add .btf Stratum listener — miners connect via rendezvous, no port forwarding needed
* btf pool: self-replacing listeners, one per relay, no queuing between miners
* Mining modes: solo/operator/participant — pool server on-demand, participant Stratum client
* Status bar: show external IP and .btf address
* README: clean rewrite, concept-first, minimal
* make linux produces AppImage
* make linux produces AppImage
* Pool dashboard: connect instructions, tabs, share bars
* Perf fixes: GetMyExternalIP threaded, btf listener counted
* Perf fixes: GetMyExternalIP threaded, btf listener counted
* Fix pool payouts: defer until coinbase maturity
* Pool: clean mode switching, IP warning
* Pool: clean mode switching
* Fix gPoolRunning: remove static
* AppImage: use real icon from bitcoin.ico
* Fix exit crash; Mining menu
* Fix exit crash; Mining menu
* Fix exit crash; Mining menu
* Fix exit crash; Mining menu
* Fix exit crash; Mining menu
* Windows build: use mingw32-make
* Windows build: simpler make invocation
* Add deps-windows target
* Windows build fixes
* Windows build fixes
* Windows makefile: fix obj dir
* Windows: output to repo root
* Windows: bundle DLLs into zip
* Replace wxWidgets with ImGui+GLFW: same build on Linux and Windows
* Fix %I64d that slipped back through rebase (main.h, net.h)
* MinGW: add _mkdir shim for non-MSVC Windows; fix %I64d
* Fix gPoolRunning extern outside ifndef _WIN32
* Remove -lboost_system: boost is header-only in this codebase
* Status bar: copy buttons for IP and .btf address
* Windows: add app icon via windres, strip wx stuff from rc file
* Clean GUI: single Options menu item, bigger font, better spacing, cleaner options dialog; update README
* GUI: embed Roboto font at 16px for clear readability on all platforms
* Windows: windres depends on obj dir target
* Fix balance hidden off-screen: put on own line below address bar
* Windows: inline mkdir in every rule, remove dir target that confused make
* Windows: move all target to top so it's the default rule
* Fix pool server not starting: always start when operator selected, re-sync dialog state on open
* Write debug.log to data directory (AppData/Roaming/Bitflash on Windows)
* Participant miner: use wallet address as username so payouts go to wallet
* Add .gitattributes to lock LF line endings, prevent CRLF drift
* Refactor pool mining to btf-only transport
* Persist pool settings and startup wiring
* Prune stale peers and expose pool queries
* Fix build regressions and add CLI mode/arg parity
* Fix rpc miner stats declaration order
* Centralize pool runtime extern declarations
* Separate operator/participant dashboards and restore peer stats
* Add live telemetry strip and clearer per-mode runtime metrics
* Reduce mining dashboard clutter and remove manual pool entry
* Count only authorized pool miners
* Seed initial pool job on startup
* Use a real share target for pool work
* Switch pool accounting to round-based shares
* Fix nostr pool announcer stats call sites
* Fix pool stats definition order
* Improve pool worker status display
* Add mutex include for participant status
* Replace ImClamp in dashboard layout
* Clarify participant work status
* Log participant status transitions
* Reduce peer discovery log noise
* Add participant status badge
* Track participant auth acknowledgements
* Log and validate pool job delivery
---------
Co-authored-by: Bitflash Pool Dev <pool@bitflash>
Diffstat (limited to 'src/net.cpp')
| -rw-r--r-- | src/net.cpp | 143 |
1 files changed, 92 insertions, 51 deletions
diff --git a/src/net.cpp b/src/net.cpp index 31afaf5..bdfb9c3 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -98,46 +98,79 @@ bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet) | |||
| 98 | 98 | ||
| 99 | bool GetMyExternalIP(unsigned int& ipRet) | 99 | bool GetMyExternalIP(unsigned int& ipRet) |
| 100 | { | 100 | { |
| 101 | // The 2009 whatismyip.com service (fixed IP) no longer exists; this avoids | 101 | // Try several plain-text IP echo services in order. |
| 102 | // stalling startup on TCP timeouts. The external IP may come from Nostr | 102 | // Each returns just the IPv4 address as the first line of the HTTP body. |
| 103 | // relays in the future. | 103 | struct { const char* host; const char* path; } services[] = { |
| 104 | return false; | 104 | { "api4.ipify.org", "/" }, |
| 105 | { "icanhazip.com", "/" }, | ||
| 106 | { "ipecho.net", "/plain" }, | ||
| 107 | { "checkip.amazonaws.com", "/" }, | ||
| 108 | }; | ||
| 109 | |||
| 110 | for (auto& svc : services) | ||
| 111 | { | ||
| 112 | struct addrinfo hints, *res = NULL; | ||
| 113 | memset(&hints, 0, sizeof(hints)); | ||
| 114 | hints.ai_family = AF_INET; | ||
| 115 | hints.ai_socktype = SOCK_STREAM; | ||
| 116 | if (getaddrinfo(svc.host, "80", &hints, &res) != 0 || !res) | ||
| 117 | continue; | ||
| 105 | 118 | ||
| 106 | CAddress addrConnect("72.233.89.199:80"); // whatismyip.com 198-200 | 119 | SOCKET hSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); |
| 120 | if (hSocket == INVALID_SOCKET) { freeaddrinfo(res); continue; } | ||
| 107 | 121 | ||
| 108 | SOCKET hSocket; | 122 | // 5-second timeout so a dead service doesn't stall startup |
| 109 | if (!ConnectSocket(addrConnect, hSocket)) | 123 | #ifdef _WIN32 |
| 110 | return error("GetMyExternalIP() : connection to %s failed\n", addrConnect.ToString().c_str()); | 124 | DWORD tv = 5000; |
| 111 | 125 | setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); | |
| 112 | char* pszGet = | 126 | setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); |
| 113 | "GET /automation/n09230945.asp HTTP/1.1\r\n" | 127 | #else |
| 114 | "Host: www.whatismyip.com\r\n" | 128 | struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; |
| 115 | "User-Agent: Bitcoin/0.1\r\n" | 129 | setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); |
| 116 | "Connection: close\r\n" | 130 | setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); |
| 117 | "\r\n"; | 131 | #endif |
| 118 | send(hSocket, pszGet, strlen(pszGet), 0); | 132 | |
| 119 | 133 | if (connect(hSocket, res->ai_addr, (int)res->ai_addrlen) != 0) | |
| 120 | string strLine; | ||
| 121 | while (RecvLine(hSocket, strLine)) | ||
| 122 | { | ||
| 123 | if (strLine.empty()) | ||
| 124 | { | 134 | { |
| 125 | if (!RecvLine(hSocket, strLine)) | 135 | freeaddrinfo(res); closesocket(hSocket); continue; |
| 126 | { | 136 | } |
| 127 | closesocket(hSocket); | 137 | freeaddrinfo(res); |
| 128 | return false; | 138 | |
| 129 | } | 139 | string req = string("GET ") + svc.path + " HTTP/1.0\r\nHost: " + svc.host + "\r\nConnection: close\r\n\r\n"; |
| 130 | closesocket(hSocket); | 140 | send(hSocket, req.c_str(), (int)req.size(), 0); |
| 131 | CAddress addr(strLine.c_str()); | 141 | |
| 132 | printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); | 142 | // Read response, skip HTTP headers, grab first line of body |
| 133 | if (addr.ip == 0) | 143 | string response; |
| 134 | return false; | 144 | char buf[256]; |
| 135 | ipRet = addr.ip; | 145 | int n; |
| 136 | return true; | 146 | while ((n = recv(hSocket, buf, sizeof(buf)-1, 0)) > 0) |
| 147 | { | ||
| 148 | buf[n] = 0; | ||
| 149 | response += buf; | ||
| 150 | if (response.size() > 4096) break; | ||
| 137 | } | 151 | } |
| 152 | closesocket(hSocket); | ||
| 153 | |||
| 154 | // Find blank line separating headers from body | ||
| 155 | size_t bodyPos = response.find("\r\n\r\n"); | ||
| 156 | if (bodyPos == string::npos) bodyPos = response.find("\n\n"); | ||
| 157 | if (bodyPos == string::npos) continue; | ||
| 158 | string body = response.substr(bodyPos + (response[bodyPos+2]=='\r' ? 4 : 2)); | ||
| 159 | |||
| 160 | // Trim whitespace | ||
| 161 | while (!body.empty() && (body.back() == '\n' || body.back() == '\r' || body.back() == ' ')) | ||
| 162 | body.pop_back(); | ||
| 163 | |||
| 164 | // Parse as IP | ||
| 165 | CAddress addr(body.c_str()); | ||
| 166 | if (addr.ip == 0) continue; | ||
| 167 | |||
| 168 | printf("GetMyExternalIP() via %s: %s\n", svc.host, body.c_str()); | ||
| 169 | ipRet = addr.ip; | ||
| 170 | return true; | ||
| 138 | } | 171 | } |
| 139 | closesocket(hSocket); | 172 | |
| 140 | return error("GetMyExternalIP() : connection closed\n"); | 173 | return error("GetMyExternalIP() : all services failed\n"); |
| 141 | } | 174 | } |
| 142 | 175 | ||
| 143 | 176 | ||
| @@ -324,15 +357,15 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) | |||
| 324 | return pnode; | 357 | return pnode; |
| 325 | } | 358 | } |
| 326 | 359 | ||
| 327 | /// debug print | 360 | if (fDebug) |
| 328 | printf("trying %s\n", addrConnect.ToString().c_str()); | 361 | printf("trying %s\n", addrConnect.ToString().c_str()); |
| 329 | 362 | ||
| 330 | // Connect | 363 | // Connect |
| 331 | SOCKET hSocket; | 364 | SOCKET hSocket; |
| 332 | if (ConnectSocket(addrConnect, hSocket)) | 365 | if (ConnectSocket(addrConnect, hSocket)) |
| 333 | { | 366 | { |
| 334 | /// debug print | 367 | if (fDebug) |
| 335 | printf("connected %s\n", addrConnect.ToString().c_str()); | 368 | printf("connected %s\n", addrConnect.ToString().c_str()); |
| 336 | 369 | ||
| 337 | // Add node | 370 | // Add node |
| 338 | CNode* pnode = new CNode(hSocket, addrConnect, false); | 371 | CNode* pnode = new CNode(hSocket, addrConnect, false); |
| @@ -390,14 +423,15 @@ CNode* ConnectNodeBtf(const string& strBtfAddr) | |||
| 390 | return pnode; | 423 | return pnode; |
| 391 | } | 424 | } |
| 392 | 425 | ||
| 393 | /// debug print | 426 | if (fDebug) |
| 394 | printf("trying %s\n", strBtfAddr.c_str()); | 427 | printf("trying %s\n", strBtfAddr.c_str()); |
| 395 | 428 | ||
| 396 | string strMeeting; | 429 | string strMeeting; |
| 397 | unsigned char enc_pub[32]; | 430 | unsigned char enc_pub[32]; |
| 398 | if (!BtfResolve(strBtfAddr, strMeeting, enc_pub)) | 431 | if (!BtfResolve(strBtfAddr, strMeeting, enc_pub)) |
| 399 | { | 432 | { |
| 400 | printf("ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str()); | 433 | if (fDebug) |
| 434 | printf("ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str()); | ||
| 401 | return NULL; | 435 | return NULL; |
| 402 | } | 436 | } |
| 403 | size_t colon = strMeeting.rfind(':'); | 437 | size_t colon = strMeeting.rfind(':'); |
| @@ -411,12 +445,13 @@ CNode* ConnectNodeBtf(const string& strBtfAddr) | |||
| 411 | btf_socket_t hSocket = btf::BtfClientTunnel(strHost.c_str(), (unsigned short)nPort, pk, enc_pub); | 445 | btf_socket_t hSocket = btf::BtfClientTunnel(strHost.c_str(), (unsigned short)nPort, pk, enc_pub); |
| 412 | if (hSocket == INVALID_SOCKET) | 446 | if (hSocket == INVALID_SOCKET) |
| 413 | { | 447 | { |
| 414 | printf("ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str()); | 448 | if (fDebug) |
| 449 | printf("ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str()); | ||
| 415 | return NULL; | 450 | return NULL; |
| 416 | } | 451 | } |
| 417 | 452 | ||
| 418 | /// debug print | 453 | if (fDebug) |
| 419 | printf("connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str()); | 454 | printf("connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str()); |
| 420 | 455 | ||
| 421 | // Add node | 456 | // Add node |
| 422 | pnode = new CNode(hSocket, addr, false); | 457 | pnode = new CNode(hSocket, addr, false); |
| @@ -1152,15 +1187,21 @@ bool StartNode(string& strError) | |||
| 1152 | return false; | 1187 | return false; |
| 1153 | } | 1188 | } |
| 1154 | 1189 | ||
| 1155 | // Get our external IP address for incoming connections | 1190 | // Get our external IP in a background thread — up to 4 HTTP requests with |
| 1191 | // 5s timeouts each could otherwise delay startup by 20s on first run. | ||
| 1192 | // addrIncoming from a previous session is used immediately as a fallback. | ||
| 1156 | if (addrIncoming.ip) | 1193 | if (addrIncoming.ip) |
| 1157 | addrLocalHost.ip = addrIncoming.ip; | 1194 | addrLocalHost.ip = addrIncoming.ip; |
| 1158 | 1195 | ||
| 1159 | if (GetMyExternalIP(addrLocalHost.ip)) | 1196 | _beginthread([](void*) { |
| 1160 | { | 1197 | unsigned int ip = addrLocalHost.ip; |
| 1161 | addrIncoming = addrLocalHost; | 1198 | if (GetMyExternalIP(ip)) { |
| 1162 | CWalletDB().WriteSetting("addrIncoming", addrIncoming); | 1199 | addrLocalHost.ip = ip; |
| 1163 | } | 1200 | addrIncoming = addrLocalHost; |
| 1201 | CWalletDB().WriteSetting("addrIncoming", addrIncoming); | ||
| 1202 | printf("External IP updated: %s\n", addrLocalHost.ToStringIP().c_str()); | ||
| 1203 | } | ||
| 1204 | }, 0, NULL); | ||
| 1164 | 1205 | ||
| 1165 | // Peer discovery over Nostr relays (replaces the old IRC seed) | 1206 | // Peer discovery over Nostr relays (replaces the old IRC seed) |
| 1166 | if (_beginthread(ThreadNostrSeed, 0, NULL) == -1) | 1207 | if (_beginthread(ThreadNostrSeed, 0, NULL) == -1) |