From 6ca1a1d6799474f8ca977b93ab630af7ace879c2 Mon Sep 17 00:00:00 2001 From: Victorio Nascimento <155357371+Vic-Nas@users.noreply.github.com> Date: Mon, 27 Jul 2026 16:45:28 -0400 Subject: Add Linux GUI installer via Wine (#3) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 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 --- src/net.cpp | 143 ++++++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 92 insertions(+), 51 deletions(-) (limited to 'src/net.cpp') 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) bool GetMyExternalIP(unsigned int& ipRet) { - // The 2009 whatismyip.com service (fixed IP) no longer exists; this avoids - // stalling startup on TCP timeouts. The external IP may come from Nostr - // relays in the future. - return false; + // Try several plain-text IP echo services in order. + // Each returns just the IPv4 address as the first line of the HTTP body. + struct { const char* host; const char* path; } services[] = { + { "api4.ipify.org", "/" }, + { "icanhazip.com", "/" }, + { "ipecho.net", "/plain" }, + { "checkip.amazonaws.com", "/" }, + }; + + for (auto& svc : services) + { + struct addrinfo hints, *res = NULL; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(svc.host, "80", &hints, &res) != 0 || !res) + continue; - CAddress addrConnect("72.233.89.199:80"); // whatismyip.com 198-200 + SOCKET hSocket = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (hSocket == INVALID_SOCKET) { freeaddrinfo(res); continue; } - SOCKET hSocket; - if (!ConnectSocket(addrConnect, hSocket)) - return error("GetMyExternalIP() : connection to %s failed\n", addrConnect.ToString().c_str()); - - char* pszGet = - "GET /automation/n09230945.asp HTTP/1.1\r\n" - "Host: www.whatismyip.com\r\n" - "User-Agent: Bitcoin/0.1\r\n" - "Connection: close\r\n" - "\r\n"; - send(hSocket, pszGet, strlen(pszGet), 0); - - string strLine; - while (RecvLine(hSocket, strLine)) - { - if (strLine.empty()) + // 5-second timeout so a dead service doesn't stall startup +#ifdef _WIN32 + DWORD tv = 5000; + setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv)); + setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv)); +#else + struct timeval tv; tv.tv_sec = 5; tv.tv_usec = 0; + setsockopt(hSocket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); + setsockopt(hSocket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); +#endif + + if (connect(hSocket, res->ai_addr, (int)res->ai_addrlen) != 0) { - if (!RecvLine(hSocket, strLine)) - { - closesocket(hSocket); - return false; - } - closesocket(hSocket); - CAddress addr(strLine.c_str()); - printf("GetMyExternalIP() received [%s] %s\n", strLine.c_str(), addr.ToString().c_str()); - if (addr.ip == 0) - return false; - ipRet = addr.ip; - return true; + freeaddrinfo(res); closesocket(hSocket); continue; + } + freeaddrinfo(res); + + string req = string("GET ") + svc.path + " HTTP/1.0\r\nHost: " + svc.host + "\r\nConnection: close\r\n\r\n"; + send(hSocket, req.c_str(), (int)req.size(), 0); + + // Read response, skip HTTP headers, grab first line of body + string response; + char buf[256]; + int n; + while ((n = recv(hSocket, buf, sizeof(buf)-1, 0)) > 0) + { + buf[n] = 0; + response += buf; + if (response.size() > 4096) break; } + closesocket(hSocket); + + // Find blank line separating headers from body + size_t bodyPos = response.find("\r\n\r\n"); + if (bodyPos == string::npos) bodyPos = response.find("\n\n"); + if (bodyPos == string::npos) continue; + string body = response.substr(bodyPos + (response[bodyPos+2]=='\r' ? 4 : 2)); + + // Trim whitespace + while (!body.empty() && (body.back() == '\n' || body.back() == '\r' || body.back() == ' ')) + body.pop_back(); + + // Parse as IP + CAddress addr(body.c_str()); + if (addr.ip == 0) continue; + + printf("GetMyExternalIP() via %s: %s\n", svc.host, body.c_str()); + ipRet = addr.ip; + return true; } - closesocket(hSocket); - return error("GetMyExternalIP() : connection closed\n"); + + return error("GetMyExternalIP() : all services failed\n"); } @@ -324,15 +357,15 @@ CNode* ConnectNode(CAddress addrConnect, int64 nTimeout) return pnode; } - /// debug print - printf("trying %s\n", addrConnect.ToString().c_str()); + if (fDebug) + printf("trying %s\n", addrConnect.ToString().c_str()); // Connect SOCKET hSocket; if (ConnectSocket(addrConnect, hSocket)) { - /// debug print - printf("connected %s\n", addrConnect.ToString().c_str()); + if (fDebug) + printf("connected %s\n", addrConnect.ToString().c_str()); // Add node CNode* pnode = new CNode(hSocket, addrConnect, false); @@ -390,14 +423,15 @@ CNode* ConnectNodeBtf(const string& strBtfAddr) return pnode; } - /// debug print - printf("trying %s\n", strBtfAddr.c_str()); + if (fDebug) + printf("trying %s\n", strBtfAddr.c_str()); string strMeeting; unsigned char enc_pub[32]; if (!BtfResolve(strBtfAddr, strMeeting, enc_pub)) { - printf("ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str()); + if (fDebug) + printf("ConnectNodeBtf: could not resolve a descriptor for %s\n", strBtfAddr.c_str()); return NULL; } size_t colon = strMeeting.rfind(':'); @@ -411,12 +445,13 @@ CNode* ConnectNodeBtf(const string& strBtfAddr) btf_socket_t hSocket = btf::BtfClientTunnel(strHost.c_str(), (unsigned short)nPort, pk, enc_pub); if (hSocket == INVALID_SOCKET) { - printf("ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str()); + if (fDebug) + printf("ConnectNodeBtf: tunnel to %s via %s failed\n", strBtfAddr.c_str(), strMeeting.c_str()); return NULL; } - /// debug print - printf("connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str()); + if (fDebug) + printf("connected %s via rendezvous %s\n", strBtfAddr.c_str(), strMeeting.c_str()); // Add node pnode = new CNode(hSocket, addr, false); @@ -1152,15 +1187,21 @@ bool StartNode(string& strError) return false; } - // Get our external IP address for incoming connections + // Get our external IP in a background thread — up to 4 HTTP requests with + // 5s timeouts each could otherwise delay startup by 20s on first run. + // addrIncoming from a previous session is used immediately as a fallback. if (addrIncoming.ip) addrLocalHost.ip = addrIncoming.ip; - if (GetMyExternalIP(addrLocalHost.ip)) - { - addrIncoming = addrLocalHost; - CWalletDB().WriteSetting("addrIncoming", addrIncoming); - } + _beginthread([](void*) { + unsigned int ip = addrLocalHost.ip; + if (GetMyExternalIP(ip)) { + addrLocalHost.ip = ip; + addrIncoming = addrLocalHost; + CWalletDB().WriteSetting("addrIncoming", addrIncoming); + printf("External IP updated: %s\n", addrLocalHost.ToStringIP().c_str()); + } + }, 0, NULL); // Peer discovery over Nostr relays (replaces the old IRC seed) if (_beginthread(ThreadNostrSeed, 0, NULL) == -1) -- cgit v1.2.3