diff options
| author | Nsable-sh <nsable-sh@noreply.git.bitflash.network> | 2026-08-07 11:34:17 -0300 |
|---|---|---|
| committer | Bitflash <admin@bitflash.network> | 2026-08-07 15:06:21 -0300 |
| commit | 8bf5b8cac5d91165b11d4ce65333b68edcc723b0 (patch) | |
| tree | de92ea6afbc7762ec3e8f9654544ca56d9448c06 /src | |
| parent | 5c2e66b70ab3c15603a6e2d7a4ea9e02f3cd0604 (diff) | |
pool: add local Stratum bridge
Diffstat (limited to 'src')
| -rw-r--r-- | src/main.cpp | 186 | ||||
| -rw-r--r-- | src/main.h | 3 | ||||
| -rw-r--r-- | src/main_gui.cpp | 22 | ||||
| -rw-r--r-- | src/net.cpp | 2 | ||||
| -rw-r--r-- | src/readme.txt | 5 | ||||
| -rw-r--r-- | src/sockcount.h | 1 |
6 files changed, 218 insertions, 1 deletions
diff --git a/src/main.cpp b/src/main.cpp index 6cb7353..2be9c54 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -106,6 +106,8 @@ string strPoolName = "Bitflash Pool"; | |||
| 106 | string strPoolDashboardUrl; | 106 | string strPoolDashboardUrl; |
| 107 | string strPoolStatusFile; | 107 | string strPoolStatusFile; |
| 108 | double dPoolFeePercent = 0.0; | 108 | double dPoolFeePercent = 0.0; |
| 109 | bool fStratumBridge = false; | ||
| 110 | int nStratumBridgePort = 3333; | ||
| 109 | 111 | ||
| 110 | static std::atomic<uint64> gParticipantSharesSent{0}; | 112 | static std::atomic<uint64> gParticipantSharesSent{0}; |
| 111 | static std::atomic<uint64> gParticipantSharesAccepted{0}; | 113 | static std::atomic<uint64> gParticipantSharesAccepted{0}; |
| @@ -3873,6 +3875,190 @@ static bool PoolParticipantMiner() | |||
| 3873 | return true; | 3875 | return true; |
| 3874 | } | 3876 | } |
| 3875 | 3877 | ||
| 3878 | // --------------------------------------------------------------------------- | ||
| 3879 | // Local Stratum bridge -- lets external miners speak plain Stratum to | ||
| 3880 | // 127.0.0.1 while Bitflash opens the .btf tunnel to the selected pool. | ||
| 3881 | // --------------------------------------------------------------------------- | ||
| 3882 | |||
| 3883 | struct StratumBridgeClientCtx | ||
| 3884 | { | ||
| 3885 | SOCKET miner; | ||
| 3886 | std::string pool; | ||
| 3887 | }; | ||
| 3888 | |||
| 3889 | static bool BridgeSendAll(SOCKET s, const char* p, int n) | ||
| 3890 | { | ||
| 3891 | int off = 0; | ||
| 3892 | while (off < n) | ||
| 3893 | { | ||
| 3894 | int r = send(s, p + off, n - off, 0); | ||
| 3895 | if (r <= 0) | ||
| 3896 | return false; | ||
| 3897 | off += r; | ||
| 3898 | } | ||
| 3899 | return true; | ||
| 3900 | } | ||
| 3901 | |||
| 3902 | static void BridgePump(SOCKET a, SOCKET b) | ||
| 3903 | { | ||
| 3904 | char buf[8192]; | ||
| 3905 | while (!fShutdown) | ||
| 3906 | { | ||
| 3907 | fd_set rd; | ||
| 3908 | FD_ZERO(&rd); | ||
| 3909 | FD_SET(a, &rd); | ||
| 3910 | FD_SET(b, &rd); | ||
| 3911 | SOCKET maxfd = a > b ? a : b; | ||
| 3912 | struct timeval tv = {1, 0}; | ||
| 3913 | int r = select((int)maxfd + 1, &rd, NULL, NULL, &tv); | ||
| 3914 | if (r <= 0) | ||
| 3915 | continue; | ||
| 3916 | |||
| 3917 | if (FD_ISSET(a, &rd)) | ||
| 3918 | { | ||
| 3919 | int n = recv(a, buf, sizeof(buf), 0); | ||
| 3920 | if (n <= 0 || !BridgeSendAll(b, buf, n)) | ||
| 3921 | break; | ||
| 3922 | } | ||
| 3923 | if (FD_ISSET(b, &rd)) | ||
| 3924 | { | ||
| 3925 | int n = recv(b, buf, sizeof(buf), 0); | ||
| 3926 | if (n <= 0 || !BridgeSendAll(a, buf, n)) | ||
| 3927 | break; | ||
| 3928 | } | ||
| 3929 | } | ||
| 3930 | } | ||
| 3931 | |||
| 3932 | static void ThreadStratumBridgeClient(void* arg) | ||
| 3933 | { | ||
| 3934 | StratumBridgeClientCtx* ctx = (StratumBridgeClientCtx*)arg; | ||
| 3935 | SOCKET miner = ctx->miner; | ||
| 3936 | std::string pool = ctx->pool; | ||
| 3937 | delete ctx; | ||
| 3938 | |||
| 3939 | unsigned char target_pubkey[32]; | ||
| 3940 | if (!btf::ParseAddress(pool, target_pubkey)) | ||
| 3941 | { | ||
| 3942 | LogPrint("worker", "[bridge] invalid pool .btf address: %s\n", pool.c_str()); | ||
| 3943 | BtfCloseSocket(miner); | ||
| 3944 | return; | ||
| 3945 | } | ||
| 3946 | |||
| 3947 | std::string meetingHostPort; | ||
| 3948 | unsigned char service_enc_pub[32]; | ||
| 3949 | if (!BtfResolve(pool, meetingHostPort, service_enc_pub)) | ||
| 3950 | { | ||
| 3951 | LogPrint("worker", "[bridge] failed to resolve pool %s\n", pool.c_str()); | ||
| 3952 | BtfCloseSocket(miner); | ||
| 3953 | return; | ||
| 3954 | } | ||
| 3955 | |||
| 3956 | size_t colon = meetingHostPort.rfind(':'); | ||
| 3957 | if (colon == std::string::npos) | ||
| 3958 | { | ||
| 3959 | LogPrint("worker", "[bridge] bad pool endpoint '%s'\n", meetingHostPort.c_str()); | ||
| 3960 | BtfCloseSocket(miner); | ||
| 3961 | return; | ||
| 3962 | } | ||
| 3963 | std::string host = meetingHostPort.substr(0, colon); | ||
| 3964 | int port = atoi(meetingHostPort.substr(colon + 1).c_str()); | ||
| 3965 | if (port <= 0 || port > 65535) | ||
| 3966 | { | ||
| 3967 | LogPrint("worker", "[bridge] bad pool port in '%s'\n", meetingHostPort.c_str()); | ||
| 3968 | BtfCloseSocket(miner); | ||
| 3969 | return; | ||
| 3970 | } | ||
| 3971 | |||
| 3972 | SOCKET poolSock = btf::BtfClientTunnel(host.c_str(), (unsigned short)port, | ||
| 3973 | target_pubkey, service_enc_pub); | ||
| 3974 | if (poolSock == INVALID_SOCKET) | ||
| 3975 | { | ||
| 3976 | LogPrint("worker", "[bridge] tunnel connect failed for pool %s via %s\n", | ||
| 3977 | pool.c_str(), meetingHostPort.c_str()); | ||
| 3978 | BtfCloseSocket(miner); | ||
| 3979 | return; | ||
| 3980 | } | ||
| 3981 | |||
| 3982 | LogPrint("worker", "[bridge] miner connected to pool %s via %s\n", | ||
| 3983 | pool.c_str(), meetingHostPort.c_str()); | ||
| 3984 | BridgePump(miner, poolSock); | ||
| 3985 | BtfCloseSocket(poolSock); | ||
| 3986 | BtfCloseSocket(miner); | ||
| 3987 | LogPrint("worker", "[bridge] miner disconnected from pool %s\n", pool.c_str()); | ||
| 3988 | } | ||
| 3989 | |||
| 3990 | void ThreadStratumBridge(void*) | ||
| 3991 | { | ||
| 3992 | if (strParticipantPool.empty()) | ||
| 3993 | { | ||
| 3994 | printf("Stratum bridge: no pool selected. Use /stratumbridge=POOL_BTF_ADDRESS\n"); | ||
| 3995 | return; | ||
| 3996 | } | ||
| 3997 | |||
| 3998 | SOCKET listener = BtfSocketTag(socket(AF_INET, SOCK_STREAM, IPPROTO_TCP), | ||
| 3999 | SOCK_STRATUM_BRIDGE); | ||
| 4000 | if (listener == INVALID_SOCKET) | ||
| 4001 | { | ||
| 4002 | printf("Stratum bridge: could not create listen socket\n"); | ||
| 4003 | return; | ||
| 4004 | } | ||
| 4005 | |||
| 4006 | int yes = 1; | ||
| 4007 | setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); | ||
| 4008 | |||
| 4009 | sockaddr_in addr; | ||
| 4010 | memset(&addr, 0, sizeof(addr)); | ||
| 4011 | addr.sin_family = AF_INET; | ||
| 4012 | addr.sin_addr.s_addr = htonl(0x7f000001); | ||
| 4013 | addr.sin_port = htons((unsigned short)nStratumBridgePort); | ||
| 4014 | |||
| 4015 | if (bind(listener, (sockaddr*)&addr, sizeof(addr)) != 0 || | ||
| 4016 | listen(listener, 16) != 0) | ||
| 4017 | { | ||
| 4018 | printf("Stratum bridge: could not listen on 127.0.0.1:%d\n", | ||
| 4019 | nStratumBridgePort); | ||
| 4020 | BtfCloseSocket(listener); | ||
| 4021 | return; | ||
| 4022 | } | ||
| 4023 | |||
| 4024 | printf("Stratum bridge: listening on 127.0.0.1:%d for pool %s\n", | ||
| 4025 | nStratumBridgePort, strParticipantPool.c_str()); | ||
| 4026 | |||
| 4027 | while (!fShutdown) | ||
| 4028 | { | ||
| 4029 | fd_set rd; | ||
| 4030 | FD_ZERO(&rd); | ||
| 4031 | FD_SET(listener, &rd); | ||
| 4032 | struct timeval tv = {1, 0}; | ||
| 4033 | if (select((int)listener + 1, &rd, NULL, NULL, &tv) <= 0) | ||
| 4034 | continue; | ||
| 4035 | |||
| 4036 | sockaddr_in cli; | ||
| 4037 | #ifdef _WIN32 | ||
| 4038 | int len = sizeof(cli); | ||
| 4039 | #else | ||
| 4040 | socklen_t len = sizeof(cli); | ||
| 4041 | #endif | ||
| 4042 | SOCKET miner = BtfSocketTag(accept(listener, (sockaddr*)&cli, &len), | ||
| 4043 | SOCK_STRATUM_BRIDGE); | ||
| 4044 | if (miner == INVALID_SOCKET) | ||
| 4045 | continue; | ||
| 4046 | |||
| 4047 | StratumBridgeClientCtx* ctx = new StratumBridgeClientCtx(); | ||
| 4048 | ctx->miner = miner; | ||
| 4049 | ctx->pool = strParticipantPool; | ||
| 4050 | if (_beginthread(ThreadStratumBridgeClient, 0, ctx) == (uintptr_t)-1) | ||
| 4051 | { | ||
| 4052 | delete ctx; | ||
| 4053 | BtfCloseSocket(miner); | ||
| 4054 | LogPrint("worker", "[bridge] failed to spawn client thread\n"); | ||
| 4055 | } | ||
| 4056 | } | ||
| 4057 | |||
| 4058 | BtfCloseSocket(listener); | ||
| 4059 | printf("Stratum bridge: stopped\n"); | ||
| 4060 | } | ||
| 4061 | |||
| 3876 | bool BitcoinMiner(int nThreadId) | 4062 | bool BitcoinMiner(int nThreadId) |
| 3877 | { | 4063 | { |
| 3878 | // Relay mode: never mines, just relays/syncs. (Shouldn't normally get here | 4064 | // Relay mode: never mines, just relays/syncs. (Shouldn't normally get here |
| @@ -88,6 +88,8 @@ extern string strPoolName; // operator: announced pool name | |||
| 88 | extern string strPoolDashboardUrl; // operator: optional dashboard URL | 88 | extern string strPoolDashboardUrl; // operator: optional dashboard URL |
| 89 | extern string strPoolStatusFile; // operator: optional JSON status output path | 89 | extern string strPoolStatusFile; // operator: optional JSON status output path |
| 90 | extern double dPoolFeePercent; // operator: announced fee percent | 90 | extern double dPoolFeePercent; // operator: announced fee percent |
| 91 | extern bool fStratumBridge; // participant helper: local Stratum bridge | ||
| 92 | extern int nStratumBridgePort; // local bridge listen port, default 3333 | ||
| 91 | extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local test) | 93 | extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local test) |
| 92 | // Threads to hash with; 0 = decide from the hardware. Set by /genproclimit. | 94 | // Threads to hash with; 0 = decide from the hardware. Set by /genproclimit. |
| 93 | extern int nMinerThreads; | 95 | extern int nMinerThreads; |
| @@ -272,6 +274,7 @@ void LimitOrphanBlocks(unsigned int nMaxOrphans); | |||
| 272 | // distinguishable, which the dedup filter would otherwise collapse into one. | 274 | // distinguishable, which the dedup filter would otherwise collapse into one. |
| 273 | bool BitcoinMiner(int nThreadId = 1); | 275 | bool BitcoinMiner(int nThreadId = 1); |
| 274 | void ThreadRPCServer(void* parg); // rpc.cpp -- .btf pool server | 276 | void ThreadRPCServer(void* parg); // rpc.cpp -- .btf pool server |
| 277 | void ThreadStratumBridge(void* parg); // main.cpp -- local Stratum -> .btf bridge | ||
| 275 | void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, double& hashRate); | 278 | void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, double& hashRate); |
| 276 | void SetParticipantMiningStatus(const std::string& status); | 279 | void SetParticipantMiningStatus(const std::string& status); |
| 277 | std::string GetParticipantMiningStatus(); | 280 | std::string GetParticipantMiningStatus(); |
diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 41661b9..ce6d2b5 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp | |||
| @@ -115,6 +115,8 @@ static void PrintUsage() | |||
| 115 | printf("Mining mode:\n"); | 115 | printf("Mining mode:\n"); |
| 116 | printf(" /operator\n"); | 116 | printf(" /operator\n"); |
| 117 | printf(" /participant=POOL_BTF_ADDRESS\n"); | 117 | printf(" /participant=POOL_BTF_ADDRESS\n"); |
| 118 | printf(" /stratumbridge=POOL_BTF_ADDRESS\n"); | ||
| 119 | printf(" /stratumbridgeport=N (default 3333; listen on 127.0.0.1)\n"); | ||
| 118 | printf(" /solomine\n"); | 120 | printf(" /solomine\n"); |
| 119 | printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n"); | 121 | printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n"); |
| 120 | printf(" /checkblocks=N (blocks re-verified at startup, default 288, 0 = all)\n"); | 122 | printf(" /checkblocks=N (blocks re-verified at startup, default 288, 0 = all)\n"); |
| @@ -226,6 +228,22 @@ static void ParseStartupArguments(int argc, char* argv[]) | |||
| 226 | fMineModeFromCommandLine = true; | 228 | fMineModeFromCommandLine = true; |
| 227 | } | 229 | } |
| 228 | 230 | ||
| 231 | if (arg(argc,argv,"/stratumbridge") || arg(argc,argv,"-stratumbridge")) | ||
| 232 | { | ||
| 233 | fStratumBridge = true; | ||
| 234 | string bridgePool = argval2(argc, argv, "/stratumbridge", "-stratumbridge"); | ||
| 235 | if (!bridgePool.empty()) | ||
| 236 | strParticipantPool = bridgePool; | ||
| 237 | } | ||
| 238 | |||
| 239 | string bridgePort = argval2(argc, argv, "/stratumbridgeport", "-stratumbridgeport"); | ||
| 240 | if (!bridgePort.empty()) | ||
| 241 | { | ||
| 242 | int nPort = atoi(bridgePort.c_str()); | ||
| 243 | if (nPort > 0 && nPort <= 65535) | ||
| 244 | nStratumBridgePort = nPort; | ||
| 245 | } | ||
| 246 | |||
| 229 | string poolName = argval2(argc, argv, "/poolname", "-poolname"); | 247 | string poolName = argval2(argc, argv, "/poolname", "-poolname"); |
| 230 | if (!poolName.empty()) | 248 | if (!poolName.empty()) |
| 231 | strPoolName = poolName; | 249 | strPoolName = poolName; |
| @@ -623,6 +641,10 @@ int main(int argc, char* argv[]) | |||
| 623 | if (_beginthread(ThreadRPCServer, 0, NULL) == (uintptr_t)-1) | 641 | if (_beginthread(ThreadRPCServer, 0, NULL) == (uintptr_t)-1) |
| 624 | printf("Error: _beginthread(ThreadRPCServer) failed\n"); | 642 | printf("Error: _beginthread(ThreadRPCServer) failed\n"); |
| 625 | } | 643 | } |
| 644 | if (fStratumBridge) { | ||
| 645 | if (_beginthread(ThreadStratumBridge, 0, NULL) == (uintptr_t)-1) | ||
| 646 | printf("Error: _beginthread(ThreadStratumBridge) failed\n"); | ||
| 647 | } | ||
| 626 | if (fGenerateBitcoins) | 648 | if (fGenerateBitcoins) |
| 627 | StartMinerThreads(); | 649 | StartMinerThreads(); |
| 628 | 650 | ||
diff --git a/src/net.cpp b/src/net.cpp index 449d91c..b9bb95d 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -153,7 +153,7 @@ static const char* pszSockSite[SOCK_SITES] = { | |||
| 153 | "external-ip probe", "listen socket", "inbound accept", | 153 | "external-ip probe", "listen socket", "inbound accept", |
| 154 | "rendezvous dial", "rendezvous listen", "rendezvous accept", | 154 | "rendezvous dial", "rendezvous listen", "rendezvous accept", |
| 155 | "loopback pair listener", "loopback pair app end", "loopback pair pump end", | 155 | "loopback pair listener", "loopback pair app end", "loopback pair pump end", |
| 156 | "nostr relay" | 156 | "nostr relay", "stratum bridge" |
| 157 | }; | 157 | }; |
| 158 | 158 | ||
| 159 | static string SockAccountingText() | 159 | static string SockAccountingText() |
diff --git a/src/readme.txt b/src/readme.txt index 6af1694..fa58b6e 100644 --- a/src/readme.txt +++ b/src/readme.txt | |||
| @@ -46,6 +46,11 @@ Command-line options (in addition to the original ones) | |||
| 46 | /operator operator mode (run pool server) | 46 | /operator operator mode (run pool server) |
| 47 | /participant=ADDR | 47 | /participant=ADDR |
| 48 | participant mode; mine to pool .btf address ADDR | 48 | participant mode; mine to pool .btf address ADDR |
| 49 | /stratumbridge=ADDR | ||
| 50 | listen on 127.0.0.1:3333 and bridge external Stratum miners | ||
| 51 | to pool .btf address ADDR | ||
| 52 | /stratumbridgeport=N | ||
| 53 | local Stratum bridge port (default 3333) | ||
| 49 | /poolname=NAME operator announcement pool name | 54 | /poolname=NAME operator announcement pool name |
| 50 | /poolfee=PCT operator announcement fee percent (example: 0.75) | 55 | /poolfee=PCT operator announcement fee percent (example: 0.75) |
| 51 | /pooldashboard=URL | 56 | /pooldashboard=URL |
diff --git a/src/sockcount.h b/src/sockcount.h index 96b6efb..5147f55 100644 --- a/src/sockcount.h +++ b/src/sockcount.h | |||
| @@ -63,6 +63,7 @@ enum | |||
| 63 | SOCK_PAIR_APP, // loopback pair: the app end handed to CNode | 63 | SOCK_PAIR_APP, // loopback pair: the app end handed to CNode |
| 64 | SOCK_PAIR_PUMP, // loopback pair: the pump end | 64 | SOCK_PAIR_PUMP, // loopback pair: the pump end |
| 65 | SOCK_NOSTR, // outbound to a Nostr relay | 65 | SOCK_NOSTR, // outbound to a Nostr relay |
| 66 | SOCK_STRATUM_BRIDGE, // local Stratum bridge listener/miner sockets | ||
| 66 | SOCK_SITES | 67 | SOCK_SITES |
| 67 | }; | 68 | }; |
| 68 | 69 | ||