diff options
| author | Bitflash <btf01@proton.me> | 2026-07-30 14:07:50 -0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-30 14:07:50 -0300 |
| commit | 225d0210f881acb10a3dfaf9bc5a23446bc12d21 (patch) | |
| tree | f8f15df707084a64924bed42e526c2e0ef43b1b9 | |
| parent | 9f7141b28e2da9db7d7cb099d1f13f28742ff0fa (diff) | |
mine: hash on every core instead of one (#52)
Satoshi left a note above ThreadBitcoinMiner in 2009:
//// todo: start one thread per processor,
//// use getenv("NUMBER_OF_PROCESSORS")
Nobody did, and it stopped being a nicety when the proof of work
became RandomX. SHA-256d on a 2009 desktop lost little to a
single thread. RandomX is built to be fed by every core at once,
so one thread on a 32-core machine mines at a thirty-second of
what the box can do -- and the only outward sign is a CPU that
looks suspiciously idle. A user reported exactly that: 30 minutes
of "Mining", no blocks, Bitflash sitting at 3.3% CPU while the
2 GB dataset was allocated and fast mode was active. Nothing was
broken. It was hashing on one core.
bitflash -gen # every core but one
bitflash -gen -genproclimit=8 # exactly eight
Automatic leaves one core to the rest of the node -- accepting
blocks, talking to peers, drawing the window -- because a node
that mines beautifully while falling behind the chain has won
nothing. /genproclimit overrides it, and a count several times
the core count is refused rather than quietly thrashing.
The wrapper was already built for this: RandomXCreateMinerVM says
"own VM, not shared", and the ~2 GB dataset is one allocation
every thread reads. Only the 2 MB scratchpad multiplies, so
sixteen threads cost about 32 MB more than one. The startup line
says so, because "why is it using 2 GB" is the first question
anyone asks.
Two things had to be right for this to be safe:
- Shutdown. Miners share slot 3 of vfThreadRunning and StopNode
waits for every slot before DBFlush tears the database down.
With several, the first one out announced that everyone had
finished while the rest were still calling ProcessBlock, so
they are counted now and only the last one clears the flag.
The three CheckForShutdown(3) calls inside the miner became
plain returns: _endthread() does not unwind on Windows, so
the bookkeeping would have been skipped there.
- Duplicated work. Each thread makes its own key, and the
coinbase pays it, so every thread has a different merkle root
and searches a different space. Block assembly was already
under cs_main and cs_mapTransactions; AddKey and ProcessBlock
take their own locks.
Verified on both platforms: four distinct thread ids reach the
hashing loop and all report back on shutdown, which exits 0 with
DBFlush running. Auto picks cores-1 (1 on a 2-core box, correctly
reported). Speedup itself needs a many-core machine to show and
has not been measured here -- the claim in this message is about
threads doing work, not a benchmark.
Not touched: RandomX's memory. RANDOMX_DATASET_BASE_SIZE and
SCRATCHPAD_L3 are part of the hash function, so a knob for "use
more RAM" would either do nothing or fork the chain. Threads are
the only real dial.
| -rw-r--r-- | src/gui.cpp | 3 | ||||
| -rw-r--r-- | src/main.cpp | 51 | ||||
| -rw-r--r-- | src/main.h | 8 | ||||
| -rw-r--r-- | src/main_gui.cpp | 25 | ||||
| -rw-r--r-- | src/net.cpp | 58 | ||||
| -rw-r--r-- | src/net.h | 2 |
6 files changed, 131 insertions, 16 deletions
diff --git a/src/gui.cpp b/src/gui.cpp index fc6f1e1..825202b 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -32,6 +32,7 @@ int64 GetBalance(); | |||
| 32 | string PubKeyToAddress(const std::vector<unsigned char>& vchPubKey); | 32 | string PubKeyToAddress(const std::vector<unsigned char>& vchPubKey); |
| 33 | bool AddressToHash160(const std::string& str, uint160& hash160Ret); | 33 | bool AddressToHash160(const std::string& str, uint160& hash160Ret); |
| 34 | void ThreadBitcoinMiner(void*); | 34 | void ThreadBitcoinMiner(void*); |
| 35 | int StartMinerThreads(); | ||
| 35 | void ThreadRPCServer(void*); | 36 | void ThreadRPCServer(void*); |
| 36 | 37 | ||
| 37 | // --------------------------------------------------------------------------- | 38 | // --------------------------------------------------------------------------- |
| @@ -762,7 +763,7 @@ static void DrawOptionsDialog() | |||
| 762 | fGenerateBitcoins = 0; | 763 | fGenerateBitcoins = 0; |
| 763 | } else { | 764 | } else { |
| 764 | fGenerateBitcoins = 1; | 765 | fGenerateBitcoins = 1; |
| 765 | if (!vfThreadRunning[3]) _beginthread(ThreadBitcoinMiner, 0, NULL); | 766 | if (!vfThreadRunning[3]) StartMinerThreads(); |
| 766 | } | 767 | } |
| 767 | 768 | ||
| 768 | if (nMineMode == MINE_OPERATOR) { | 769 | if (nMineMode == MINE_OPERATOR) { |
diff --git a/src/main.cpp b/src/main.cpp index c882b0b..d4fc6d0 100644 --- a/src/main.cpp +++ b/src/main.cpp | |||
| @@ -72,6 +72,13 @@ bool fSoloMineTest = false; // /solomine: mine without requiring a peer (local t | |||
| 72 | 72 | ||
| 73 | // Settings | 73 | // Settings |
| 74 | int fGenerateBitcoins; | 74 | int fGenerateBitcoins; |
| 75 | // How many threads to hash with. 0 means "decide from the hardware" -- see | ||
| 76 | // MinerThreadCount(). This was effectively 1 forever, because Bitcoin 0.1.0 | ||
| 77 | // started exactly one miner thread and nothing here ever changed that. It made | ||
| 78 | // sense for SHA-256d in 2009; RandomX is built to be fed by every core at once, | ||
| 79 | // so a 32-core machine was mining at a thirty-second of its capacity and the | ||
| 80 | // only visible symptom was a suspiciously idle CPU. | ||
| 81 | int nMinerThreads = 0; | ||
| 75 | int64 nTransactionFee = 0; | 82 | int64 nTransactionFee = 0; |
| 76 | CAddress addrIncoming; | 83 | CAddress addrIncoming; |
| 77 | int nMineMode = MINE_RELAY; | 84 | int nMineMode = MINE_RELAY; |
| @@ -119,6 +126,26 @@ void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, doubl | |||
| 119 | // mapKeys | 126 | // mapKeys |
| 120 | // | 127 | // |
| 121 | 128 | ||
| 129 | // Threads to hash with, resolving the automatic case. | ||
| 130 | // | ||
| 131 | // Automatic leaves one core to the rest of the node -- accepting blocks, | ||
| 132 | // talking to peers, serving the GUI -- because a machine that mines perfectly | ||
| 133 | // while falling behind the chain has won nothing. Anyone who disagrees can say | ||
| 134 | // so with /genproclimit and get exactly what they ask for. | ||
| 135 | // | ||
| 136 | // Memory is not the reason to hold back: the ~2 GB RandomX dataset is shared by | ||
| 137 | // every thread, and each one adds only its own 2 MB scratchpad. Sixteen threads | ||
| 138 | // cost about 32 MB more than one. | ||
| 139 | int MinerThreadCount() | ||
| 140 | { | ||
| 141 | if (nMinerThreads > 0) | ||
| 142 | return nMinerThreads; | ||
| 143 | unsigned int nCores = std::thread::hardware_concurrency(); | ||
| 144 | if (nCores <= 1) | ||
| 145 | return 1; | ||
| 146 | return (int)(nCores - 1); | ||
| 147 | } | ||
| 148 | |||
| 122 | bool AddKey(const CKey& key) | 149 | bool AddKey(const CKey& key) |
| 123 | { | 150 | { |
| 124 | CRITICAL_BLOCK(cs_mapKeys) | 151 | CRITICAL_BLOCK(cs_mapKeys) |
| @@ -2769,7 +2796,7 @@ static bool PoolParticipantMiner() | |||
| 2769 | return true; | 2796 | return true; |
| 2770 | } | 2797 | } |
| 2771 | 2798 | ||
| 2772 | bool BitcoinMiner() | 2799 | bool BitcoinMiner(int nThreadId) |
| 2773 | { | 2800 | { |
| 2774 | // Relay mode: never mines, just relays/syncs. (Shouldn't normally get here | 2801 | // Relay mode: never mines, just relays/syncs. (Shouldn't normally get here |
| 2775 | // since the GUI hides Start Mining for Relay, but guard it regardless.) | 2802 | // since the GUI hides Start Mining for Relay, but guard it regardless.) |
| @@ -2809,11 +2836,17 @@ bool BitcoinMiner() | |||
| 2809 | while (fGenerateBitcoins) | 2836 | while (fGenerateBitcoins) |
| 2810 | { | 2837 | { |
| 2811 | Sleep(50); | 2838 | Sleep(50); |
| 2812 | CheckForShutdown(3); | 2839 | // Return instead of _endthread()ing. With more than one miner thread the |
| 2840 | // bookkeeping in ThreadBitcoinMiner has to run on the way out, and | ||
| 2841 | // _endthread() does not unwind on Windows, so a normal return is the | ||
| 2842 | // only exit that works the same on both platforms. | ||
| 2843 | if (fShutdown) | ||
| 2844 | return true; | ||
| 2813 | while (vNodes.empty() && !fSoloMineTest) | 2845 | while (vNodes.empty() && !fSoloMineTest) |
| 2814 | { | 2846 | { |
| 2815 | Sleep(1000); | 2847 | Sleep(1000); |
| 2816 | CheckForShutdown(3); | 2848 | if (fShutdown) |
| 2849 | return true; | ||
| 2817 | } | 2850 | } |
| 2818 | 2851 | ||
| 2819 | unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; | 2852 | unsigned int nTransactionsUpdatedLast = nTransactionsUpdated; |
| @@ -2908,8 +2941,11 @@ bool BitcoinMiner() | |||
| 2908 | Sleep(1000); | 2941 | Sleep(1000); |
| 2909 | continue; | 2942 | continue; |
| 2910 | } | 2943 | } |
| 2911 | LogPrint("net", "BitcoinMiner: mining with RandomX (%s)\n", | 2944 | // The thread number is in the line on purpose: every miner would |
| 2912 | RandomXFastReady() ? "fast 2GB" : "light 256MB"); | 2945 | // otherwise print the same text, and the dedup filter would fold them |
| 2946 | // into one, leaving no way to tell four threads from one. | ||
| 2947 | LogPrint("net", "BitcoinMiner: thread %d hashing with RandomX (%s)\n", | ||
| 2948 | nThreadId, RandomXFastReady() ? "fast 2GB" : "light 256MB"); | ||
| 2913 | 2949 | ||
| 2914 | unsigned int nStart = GetTime(); | 2950 | unsigned int nStart = GetTime(); |
| 2915 | uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); | 2951 | uint256 hashTarget = CBigNum().SetCompact(pblock->nBits).getuint256(); |
| @@ -2950,7 +2986,8 @@ bool BitcoinMiner() | |||
| 2950 | // ~256 hashes is already plenty of time) | 2986 | // ~256 hashes is already plenty of time) |
| 2951 | if ((++pblock->nNonce & 0xff) == 0) | 2987 | if ((++pblock->nNonce & 0xff) == 0) |
| 2952 | { | 2988 | { |
| 2953 | CheckForShutdown(3); | 2989 | if (fShutdown) |
| 2990 | break; // falls through to RandomXDestroyMinerVM below | ||
| 2954 | if (pblock->nNonce == 0) | 2991 | if (pblock->nNonce == 0) |
| 2955 | break; | 2992 | break; |
| 2956 | if (pindexPrev != pindexBest) | 2993 | if (pindexPrev != pindexBest) |
| @@ -2963,6 +3000,8 @@ bool BitcoinMiner() | |||
| 2963 | } | 3000 | } |
| 2964 | } | 3001 | } |
| 2965 | RandomXDestroyMinerVM(rxvm); | 3002 | RandomXDestroyMinerVM(rxvm); |
| 3003 | if (fShutdown) | ||
| 3004 | break; | ||
| 2966 | } | 3005 | } |
| 2967 | 3006 | ||
| 2968 | return true; | 3007 | return true; |
| @@ -73,6 +73,10 @@ extern string strPoolName; // operator: announced pool name | |||
| 73 | extern string strPoolDashboardUrl; // operator: optional dashboard URL | 73 | extern string strPoolDashboardUrl; // operator: optional dashboard URL |
| 74 | extern double dPoolFeePercent; // operator: announced fee percent | 74 | extern double dPoolFeePercent; // operator: announced fee percent |
| 75 | extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local test) | 75 | extern bool fSoloMineTest; // /solomine: mine without requiring a peer (local test) |
| 76 | // Threads to hash with; 0 = decide from the hardware. Set by /genproclimit. | ||
| 77 | extern int nMinerThreads; | ||
| 78 | // Resolves the automatic case into a real count. | ||
| 79 | int MinerThreadCount(); | ||
| 76 | extern bool gPoolServerRunning; // GUI/startup state | 80 | extern bool gPoolServerRunning; // GUI/startup state |
| 77 | extern volatile bool gPoolRunning; // runtime pool server loop flag (rpc.cpp) | 81 | extern volatile bool gPoolRunning; // runtime pool server loop flag (rpc.cpp) |
| 78 | 82 | ||
| @@ -117,7 +121,9 @@ void PrintBlockTree(); | |||
| 117 | void AddOrphanTx(const CDataStream& vMsg); | 121 | void AddOrphanTx(const CDataStream& vMsg); |
| 118 | void EraseOrphanTx(uint256 hash); | 122 | void EraseOrphanTx(uint256 hash); |
| 119 | void LimitOrphanTx(unsigned int nMaxOrphans); | 123 | void LimitOrphanTx(unsigned int nMaxOrphans); |
| 120 | bool BitcoinMiner(); | 124 | // nThreadId is 1..N, only for logging: it makes each miner's lines |
| 125 | // distinguishable, which the dedup filter would otherwise collapse into one. | ||
| 126 | bool BitcoinMiner(int nThreadId = 1); | ||
| 121 | void ThreadRPCServer(void* parg); // rpc.cpp -- .btf pool server | 127 | void ThreadRPCServer(void* parg); // rpc.cpp -- .btf pool server |
| 122 | void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, double& hashRate); | 128 | void GetParticipantMiningStats(uint64& sharesSent, uint64& sharesAccepted, double& hashRate); |
| 123 | void SetParticipantMiningStatus(const std::string& status); | 129 | void SetParticipantMiningStatus(const std::string& status); |
diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 2484592..852ad4d 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | // Bitflash entry point -- starts node threads then runs GUI (or headless). | 1 | // Bitflash entry point -- starts node threads then runs GUI (or headless). |
| 2 | 2 | ||
| 3 | #include "headers_core.h" | 3 | #include "headers_core.h" |
| 4 | #include <thread> // hardware_concurrency, to sanity-check /genproclimit | ||
| 4 | #ifndef _WIN32 | 5 | #ifndef _WIN32 |
| 5 | #include <csignal> | 6 | #include <csignal> |
| 6 | #endif | 7 | #endif |
| @@ -52,6 +53,7 @@ static void PrintUsage() | |||
| 52 | printf(" /operator\n"); | 53 | printf(" /operator\n"); |
| 53 | printf(" /participant=POOL_BTF_ADDRESS\n"); | 54 | printf(" /participant=POOL_BTF_ADDRESS\n"); |
| 54 | printf(" /solomine\n"); | 55 | printf(" /solomine\n"); |
| 56 | printf(" /genproclimit=N (mining threads; 0 or absent = every core but one)\n"); | ||
| 55 | printf("\n"); | 57 | printf("\n"); |
| 56 | printf("Pool operator announcement:\n"); | 58 | printf("Pool operator announcement:\n"); |
| 57 | printf(" /poolname=NAME\n"); | 59 | printf(" /poolname=NAME\n"); |
| @@ -99,6 +101,26 @@ static void ParseStartupArguments(int argc, char* argv[]) | |||
| 99 | if (arg(argc,argv,"/solomine") || arg(argc,argv,"-solomine")) | 101 | if (arg(argc,argv,"/solomine") || arg(argc,argv,"-solomine")) |
| 100 | fSoloMineTest = true; | 102 | fSoloMineTest = true; |
| 101 | 103 | ||
| 104 | // /genproclimit=N -- threads to hash with. 0 or absent means automatic, | ||
| 105 | // which is every core but one. Named after Bitcoin's own option so it reads | ||
| 106 | // familiarly to anyone who has run one of these before. | ||
| 107 | string strProcLimit = argval2(argc, argv, "/genproclimit", "-genproclimit"); | ||
| 108 | if (!strProcLimit.empty()) | ||
| 109 | { | ||
| 110 | int n = atoi(strProcLimit.c_str()); | ||
| 111 | unsigned int nCores = std::thread::hardware_concurrency(); | ||
| 112 | if (n < 0) | ||
| 113 | fprintf(stderr, "Ignoring /genproclimit=%s: not a thread count\n", | ||
| 114 | strProcLimit.c_str()); | ||
| 115 | else if (nCores > 0 && n > (int)nCores * 4) | ||
| 116 | // Well past diminishing returns, and every thread still costs a | ||
| 117 | // scratchpad. Refuse rather than quietly thrash the machine. | ||
| 118 | fprintf(stderr, "Ignoring /genproclimit=%d: this machine has %u core(s)\n", | ||
| 119 | n, nCores); | ||
| 120 | else | ||
| 121 | nMinerThreads = n; // 0 stays automatic | ||
| 122 | } | ||
| 123 | |||
| 102 | if (arg(argc,argv,"/operator") || arg(argc,argv,"-operator")) | 124 | if (arg(argc,argv,"/operator") || arg(argc,argv,"-operator")) |
| 103 | nMineMode = MINE_OPERATOR; | 125 | nMineMode = MINE_OPERATOR; |
| 104 | 126 | ||
| @@ -289,8 +311,7 @@ int main(int argc, char* argv[]) | |||
| 289 | printf("Error: _beginthread(ThreadRPCServer) failed\n"); | 311 | printf("Error: _beginthread(ThreadRPCServer) failed\n"); |
| 290 | } | 312 | } |
| 291 | if (fGenerateBitcoins) | 313 | if (fGenerateBitcoins) |
| 292 | if (_beginthread(ThreadBitcoinMiner, 0, NULL) == (uintptr_t)-1) | 314 | StartMinerThreads(); |
| 293 | printf("Error: _beginthread(ThreadBitcoinMiner) failed\n"); | ||
| 294 | 315 | ||
| 295 | #ifdef BITFLASH_NO_GUI | 316 | #ifdef BITFLASH_NO_GUI |
| 296 | // Nothing else this binary can do; /nogui is accepted and redundant. | 317 | // Nothing else this binary can do; /nogui is accepted and redundant. |
diff --git a/src/net.cpp b/src/net.cpp index 2581578..9ee056f 100644 --- a/src/net.cpp +++ b/src/net.cpp | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #undef snprintf | 10 | #undef snprintf |
| 11 | #endif | 11 | #endif |
| 12 | #include "headers.h" | 12 | #include "headers.h" |
| 13 | #include <thread> // hardware_concurrency, for the miner thread count | ||
| 13 | #ifdef _WIN32 | 14 | #ifdef _WIN32 |
| 14 | #include <winsock2.h> | 15 | #include <winsock2.h> |
| 15 | #endif | 16 | #endif |
| @@ -1224,18 +1225,63 @@ void ThreadMessageHandler2(void* parg) | |||
| 1224 | 1225 | ||
| 1225 | 1226 | ||
| 1226 | 1227 | ||
| 1227 | //// todo: start one thread per processor, use getenv("NUMBER_OF_PROCESSORS") | 1228 | // Satoshi's "todo: start one thread per processor" stood here since 2009 and is |
| 1229 | // what MinerThreadCount() and StartMinerThreads() below finally do -- by way of | ||
| 1230 | // std::thread::hardware_concurrency rather than NUMBER_OF_PROCESSORS, which only | ||
| 1231 | // ever existed on Windows. | ||
| 1232 | // | ||
| 1233 | // Miners share slot 3 of vfThreadRunning, and StopNode waits for every slot to | ||
| 1234 | // go false before the node tears the database down. With one miner that was the | ||
| 1235 | // same thing as "the miner finished"; with several, the first one out would | ||
| 1236 | // announce that everybody had finished while the rest were still hashing and | ||
| 1237 | // still calling ProcessBlock. So count them, and let only the last one leaving | ||
| 1238 | // clear the flag. | ||
| 1239 | static CCriticalSection cs_nMinersRunning; | ||
| 1240 | static int nMinersRunning = 0; | ||
| 1241 | |||
| 1228 | void ThreadBitcoinMiner(void* parg) | 1242 | void ThreadBitcoinMiner(void* parg) |
| 1229 | { | 1243 | { |
| 1230 | vfThreadRunning[3] = true; | 1244 | int nThreadId = (int)(intptr_t)parg; |
| 1231 | CheckForShutdown(3); | 1245 | CRITICAL_BLOCK(cs_nMinersRunning) |
| 1246 | { | ||
| 1247 | nMinersRunning++; | ||
| 1248 | vfThreadRunning[3] = true; | ||
| 1249 | } | ||
| 1232 | try | 1250 | try |
| 1233 | { | 1251 | { |
| 1234 | bool fRet = BitcoinMiner(); | 1252 | bool fRet = BitcoinMiner(nThreadId); |
| 1235 | printf("BitcoinMiner returned %s\n\n\n", fRet ? "true" : "false"); | 1253 | printf("BitcoinMiner thread %d returned %s\n", nThreadId, fRet ? "true" : "false"); |
| 1236 | } | 1254 | } |
| 1237 | CATCH_PRINT_EXCEPTION("BitcoinMiner()") | 1255 | CATCH_PRINT_EXCEPTION("BitcoinMiner()") |
| 1238 | vfThreadRunning[3] = false; | 1256 | CRITICAL_BLOCK(cs_nMinersRunning) |
| 1257 | { | ||
| 1258 | if (--nMinersRunning <= 0) | ||
| 1259 | { | ||
| 1260 | nMinersRunning = 0; | ||
| 1261 | vfThreadRunning[3] = false; | ||
| 1262 | } | ||
| 1263 | } | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | // Start the miners. Returns how many actually got off the ground. | ||
| 1267 | int StartMinerThreads() | ||
| 1268 | { | ||
| 1269 | int nThreads = MinerThreadCount(); | ||
| 1270 | int nStarted = 0; | ||
| 1271 | for (int i = 0; i < nThreads; i++) | ||
| 1272 | { | ||
| 1273 | if (_beginthread(ThreadBitcoinMiner, 0, (void*)(intptr_t)(i + 1)) == (uintptr_t)-1) | ||
| 1274 | printf("Error: _beginthread(ThreadBitcoinMiner) failed on thread %d\n", i + 1); | ||
| 1275 | else | ||
| 1276 | nStarted++; | ||
| 1277 | } | ||
| 1278 | // The dataset is one allocation shared by all of them; only the 2 MB | ||
| 1279 | // scratchpads multiply. Printed because "why is it using 2 GB" is the first | ||
| 1280 | // question anyone asks. | ||
| 1281 | printf("Mining with %d thread(s) of %u core(s) -- about %d MB " | ||
| 1282 | "(~2080 MB shared RandomX dataset + ~2 MB per thread)\n", | ||
| 1283 | nStarted, std::thread::hardware_concurrency(), 2080 + 2 * nStarted); | ||
| 1284 | return nStarted; | ||
| 1239 | } | 1285 | } |
| 1240 | 1286 | ||
| 1241 | 1287 | ||
| @@ -58,6 +58,8 @@ int BtfPexAccept(const std::vector<std::string>& vDesc); | |||
| 58 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); | 58 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); |
| 59 | bool AnySubscribed(unsigned int nChannel); | 59 | bool AnySubscribed(unsigned int nChannel); |
| 60 | void ThreadBitcoinMiner(void* parg); | 60 | void ThreadBitcoinMiner(void* parg); |
| 61 | // Starts MinerThreadCount() miners; returns how many came up. | ||
| 62 | int StartMinerThreads(); | ||
| 61 | bool StartNode(string& strError=REF(string())); | 63 | bool StartNode(string& strError=REF(string())); |
| 62 | bool StopNode(); | 64 | bool StopNode(); |
| 63 | void CheckForShutdown(int n); | 65 | void CheckForShutdown(int n); |