You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

miner.cpp 21KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595
  1. // Copyright (c) 2009-2010 Satoshi Nakamoto
  2. // Copyright (c) 2009-2014 The Bitcoin developers
  3. // Distributed under the MIT/X11 software license, see the accompanying
  4. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5. #include "miner.h"
  6. #include "amount.h"
  7. #include "core/block.h"
  8. #include "core/transaction.h"
  9. #include "hash.h"
  10. #include "main.h"
  11. #include "net.h"
  12. #include "pow.h"
  13. #include "util.h"
  14. #include "utilmoneystr.h"
  15. #ifdef ENABLE_WALLET
  16. #include "wallet.h"
  17. #endif
  18. #include <boost/thread.hpp>
  19. #include <boost/tuple/tuple.hpp>
  20. using namespace std;
  21. //////////////////////////////////////////////////////////////////////////////
  22. //
  23. // BitcoinMiner
  24. //
  25. //
  26. // Unconfirmed transactions in the memory pool often depend on other
  27. // transactions in the memory pool. When we select transactions from the
  28. // pool, we select by highest priority or fee rate, so we might consider
  29. // transactions that depend on transactions that aren't yet in the block.
  30. // The COrphan class keeps track of these 'temporary orphans' while
  31. // CreateBlock is figuring out which transactions to include.
  32. //
  33. class COrphan
  34. {
  35. public:
  36. const CTransaction* ptx;
  37. set<uint256> setDependsOn;
  38. CFeeRate feeRate;
  39. double dPriority;
  40. COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0)
  41. {
  42. }
  43. };
  44. uint64_t nLastBlockTx = 0;
  45. uint64_t nLastBlockSize = 0;
  46. // We want to sort transactions by priority and fee rate, so:
  47. typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
  48. class TxPriorityCompare
  49. {
  50. bool byFee;
  51. public:
  52. TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
  53. bool operator()(const TxPriority& a, const TxPriority& b)
  54. {
  55. if (byFee)
  56. {
  57. if (a.get<1>() == b.get<1>())
  58. return a.get<0>() < b.get<0>();
  59. return a.get<1>() < b.get<1>();
  60. }
  61. else
  62. {
  63. if (a.get<0>() == b.get<0>())
  64. return a.get<1>() < b.get<1>();
  65. return a.get<0>() < b.get<0>();
  66. }
  67. }
  68. };
  69. CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn)
  70. {
  71. // Create new block
  72. auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
  73. if(!pblocktemplate.get())
  74. return NULL;
  75. CBlock *pblock = &pblocktemplate->block; // pointer for convenience
  76. // -regtest only: allow overriding block.nVersion with
  77. // -blockversion=N to test forking scenarios
  78. if (Params().MineBlocksOnDemand())
  79. pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
  80. // Create coinbase tx
  81. CMutableTransaction txNew;
  82. txNew.vin.resize(1);
  83. txNew.vin[0].prevout.SetNull();
  84. txNew.vout.resize(1);
  85. txNew.vout[0].scriptPubKey = scriptPubKeyIn;
  86. // Add dummy coinbase tx as first transaction
  87. pblock->vtx.push_back(CTransaction());
  88. pblocktemplate->vTxFees.push_back(-1); // updated at end
  89. pblocktemplate->vTxSigOps.push_back(-1); // updated at end
  90. // Largest block you're willing to create:
  91. unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);
  92. // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
  93. nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize));
  94. // How much of the block should be dedicated to high-priority transactions,
  95. // included regardless of the fees they pay
  96. unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE);
  97. nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
  98. // Minimum block size you want to create; block will be filled with free transactions
  99. // until there are no more or the block reaches this size:
  100. unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE);
  101. nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
  102. // Collect memory pool transactions into the block
  103. CAmount nFees = 0;
  104. {
  105. LOCK2(cs_main, mempool.cs);
  106. CBlockIndex* pindexPrev = chainActive.Tip();
  107. CCoinsViewCache view(pcoinsTip);
  108. // Priority order to process transactions
  109. list<COrphan> vOrphan; // list memory doesn't move
  110. map<uint256, vector<COrphan*> > mapDependers;
  111. bool fPrintPriority = GetBoolArg("-printpriority", false);
  112. // This vector will be sorted into a priority queue:
  113. vector<TxPriority> vecPriority;
  114. vecPriority.reserve(mempool.mapTx.size());
  115. for (map<uint256, CTxMemPoolEntry>::iterator mi = mempool.mapTx.begin();
  116. mi != mempool.mapTx.end(); ++mi)
  117. {
  118. const CTransaction& tx = mi->second.GetTx();
  119. if (tx.IsCoinBase() || !IsFinalTx(tx, pindexPrev->nHeight + 1))
  120. continue;
  121. COrphan* porphan = NULL;
  122. double dPriority = 0;
  123. CAmount nTotalIn = 0;
  124. bool fMissingInputs = false;
  125. BOOST_FOREACH(const CTxIn& txin, tx.vin)
  126. {
  127. // Read prev transaction
  128. if (!view.HaveCoins(txin.prevout.hash))
  129. {
  130. // This should never happen; all transactions in the memory
  131. // pool should connect to either transactions in the chain
  132. // or other transactions in the memory pool.
  133. if (!mempool.mapTx.count(txin.prevout.hash))
  134. {
  135. LogPrintf("ERROR: mempool transaction missing input\n");
  136. if (fDebug) assert("mempool transaction missing input" == 0);
  137. fMissingInputs = true;
  138. if (porphan)
  139. vOrphan.pop_back();
  140. break;
  141. }
  142. // Has to wait for dependencies
  143. if (!porphan)
  144. {
  145. // Use list for automatic deletion
  146. vOrphan.push_back(COrphan(&tx));
  147. porphan = &vOrphan.back();
  148. }
  149. mapDependers[txin.prevout.hash].push_back(porphan);
  150. porphan->setDependsOn.insert(txin.prevout.hash);
  151. nTotalIn += mempool.mapTx[txin.prevout.hash].GetTx().vout[txin.prevout.n].nValue;
  152. continue;
  153. }
  154. const CCoins* coins = view.AccessCoins(txin.prevout.hash);
  155. assert(coins);
  156. CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
  157. nTotalIn += nValueIn;
  158. int nConf = pindexPrev->nHeight - coins->nHeight + 1;
  159. dPriority += (double)nValueIn * nConf;
  160. }
  161. if (fMissingInputs) continue;
  162. // Priority is sum(valuein * age) / modified_txsize
  163. unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
  164. dPriority = tx.ComputePriority(dPriority, nTxSize);
  165. uint256 hash = tx.GetHash();
  166. mempool.ApplyDeltas(hash, dPriority, nTotalIn);
  167. CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
  168. if (porphan)
  169. {
  170. porphan->dPriority = dPriority;
  171. porphan->feeRate = feeRate;
  172. }
  173. else
  174. vecPriority.push_back(TxPriority(dPriority, feeRate, &mi->second.GetTx()));
  175. }
  176. // Collect transactions into block
  177. uint64_t nBlockSize = 1000;
  178. uint64_t nBlockTx = 0;
  179. int nBlockSigOps = 100;
  180. bool fSortedByFee = (nBlockPrioritySize <= 0);
  181. TxPriorityCompare comparer(fSortedByFee);
  182. std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
  183. while (!vecPriority.empty())
  184. {
  185. // Take highest priority transaction off the priority queue:
  186. double dPriority = vecPriority.front().get<0>();
  187. CFeeRate feeRate = vecPriority.front().get<1>();
  188. const CTransaction& tx = *(vecPriority.front().get<2>());
  189. std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
  190. vecPriority.pop_back();
  191. // Size limits
  192. unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
  193. if (nBlockSize + nTxSize >= nBlockMaxSize)
  194. continue;
  195. // Legacy limits on sigOps:
  196. unsigned int nTxSigOps = GetLegacySigOpCount(tx);
  197. if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
  198. continue;
  199. // Skip free transactions if we're past the minimum block size:
  200. const uint256& hash = tx.GetHash();
  201. double dPriorityDelta = 0;
  202. CAmount nFeeDelta = 0;
  203. mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
  204. if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
  205. continue;
  206. // Prioritise by fee once past the priority size or we run out of high-priority
  207. // transactions:
  208. if (!fSortedByFee &&
  209. ((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
  210. {
  211. fSortedByFee = true;
  212. comparer = TxPriorityCompare(fSortedByFee);
  213. std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
  214. }
  215. if (!view.HaveInputs(tx))
  216. continue;
  217. CAmount nTxFees = view.GetValueIn(tx)-tx.GetValueOut();
  218. nTxSigOps += GetP2SHSigOpCount(tx, view);
  219. if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
  220. continue;
  221. // Note that flags: we don't want to set mempool/IsStandard()
  222. // policy here, but we still have to ensure that the block we
  223. // create only contains transactions that are valid in new blocks.
  224. CValidationState state;
  225. if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
  226. continue;
  227. CTxUndo txundo;
  228. UpdateCoins(tx, state, view, txundo, pindexPrev->nHeight+1);
  229. // Added
  230. pblock->vtx.push_back(tx);
  231. pblocktemplate->vTxFees.push_back(nTxFees);
  232. pblocktemplate->vTxSigOps.push_back(nTxSigOps);
  233. nBlockSize += nTxSize;
  234. ++nBlockTx;
  235. nBlockSigOps += nTxSigOps;
  236. nFees += nTxFees;
  237. if (fPrintPriority)
  238. {
  239. LogPrintf("priority %.1f fee %s txid %s\n",
  240. dPriority, feeRate.ToString(), tx.GetHash().ToString());
  241. }
  242. // Add transactions that depend on this one to the priority queue
  243. if (mapDependers.count(hash))
  244. {
  245. BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
  246. {
  247. if (!porphan->setDependsOn.empty())
  248. {
  249. porphan->setDependsOn.erase(hash);
  250. if (porphan->setDependsOn.empty())
  251. {
  252. vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx));
  253. std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
  254. }
  255. }
  256. }
  257. }
  258. }
  259. nLastBlockTx = nBlockTx;
  260. nLastBlockSize = nBlockSize;
  261. LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize);
  262. // Compute final coinbase transaction.
  263. txNew.vout[0].nValue = GetBlockValue(pindexPrev->nHeight+1, nFees);
  264. txNew.vin[0].scriptSig = CScript() << OP_0 << OP_0;
  265. pblock->vtx[0] = txNew;
  266. pblocktemplate->vTxFees[0] = -nFees;
  267. // Fill in header
  268. pblock->hashPrevBlock = pindexPrev->GetBlockHash();
  269. UpdateTime(pblock, pindexPrev);
  270. pblock->nBits = GetNextWorkRequired(pindexPrev, pblock);
  271. pblock->nNonce = 0;
  272. pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
  273. CBlockIndex indexDummy(*pblock);
  274. indexDummy.pprev = pindexPrev;
  275. indexDummy.nHeight = pindexPrev->nHeight + 1;
  276. CCoinsViewCache viewNew(pcoinsTip);
  277. CValidationState state;
  278. if (!ConnectBlock(*pblock, state, &indexDummy, viewNew, true))
  279. throw std::runtime_error("CreateNewBlock() : ConnectBlock failed");
  280. }
  281. return pblocktemplate.release();
  282. }
  283. void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
  284. {
  285. // Update nExtraNonce
  286. static uint256 hashPrevBlock;
  287. if (hashPrevBlock != pblock->hashPrevBlock)
  288. {
  289. nExtraNonce = 0;
  290. hashPrevBlock = pblock->hashPrevBlock;
  291. }
  292. ++nExtraNonce;
  293. unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
  294. CMutableTransaction txCoinbase(pblock->vtx[0]);
  295. txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
  296. assert(txCoinbase.vin[0].scriptSig.size() <= 100);
  297. pblock->vtx[0] = txCoinbase;
  298. pblock->hashMerkleRoot = pblock->BuildMerkleTree();
  299. }
  300. #ifdef ENABLE_WALLET
  301. //////////////////////////////////////////////////////////////////////////////
  302. //
  303. // Internal miner
  304. //
  305. double dHashesPerSec = 0.0;
  306. int64_t nHPSTimerStart = 0;
  307. //
  308. // ScanHash scans nonces looking for a hash with at least some zero bits.
  309. // The nonce is usually preserved between calls, but periodically or if the
  310. // nonce is 0xffff0000 or above, the block is rebuilt and nNonce starts over at
  311. // zero.
  312. //
  313. bool static ScanHash(const CBlockHeader *pblock, uint32_t& nNonce, uint256 *phash)
  314. {
  315. // Write the first 76 bytes of the block header to a double-SHA256 state.
  316. CHash256 hasher;
  317. CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
  318. ss << *pblock;
  319. assert(ss.size() == 80);
  320. hasher.Write((unsigned char*)&ss[0], 76);
  321. while (true) {
  322. nNonce++;
  323. // Write the last 4 bytes of the block header (the nonce) to a copy of
  324. // the double-SHA256 state, and compute the result.
  325. CHash256(hasher).Write((unsigned char*)&nNonce, 4).Finalize((unsigned char*)phash);
  326. // Return the nonce if the hash has at least some zero bits,
  327. // caller will check if it has enough to reach the target
  328. if (((uint16_t*)phash)[15] == 0)
  329. return true;
  330. // If nothing found after trying for a while, return -1
  331. if ((nNonce & 0xffff) == 0)
  332. return false;
  333. if ((nNonce & 0xfff) == 0)
  334. boost::this_thread::interruption_point();
  335. }
  336. }
  337. CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey)
  338. {
  339. CPubKey pubkey;
  340. if (!reservekey.GetReservedKey(pubkey))
  341. return NULL;
  342. CScript scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
  343. return CreateNewBlock(scriptPubKey);
  344. }
  345. bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
  346. {
  347. LogPrintf("%s\n", pblock->ToString());
  348. LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue));
  349. // Found a solution
  350. {
  351. LOCK(cs_main);
  352. if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
  353. return error("BitcoinMiner : generated block is stale");
  354. }
  355. // Remove key from key pool
  356. reservekey.KeepKey();
  357. // Track how many getdata requests this block gets
  358. {
  359. LOCK(wallet.cs_wallet);
  360. wallet.mapRequestCount[pblock->GetHash()] = 0;
  361. }
  362. // Process this block the same as if we had received it from another node
  363. CValidationState state;
  364. if (!ProcessBlock(state, NULL, pblock))
  365. return error("BitcoinMiner : ProcessBlock, block not accepted");
  366. return true;
  367. }
  368. void static BitcoinMiner(CWallet *pwallet)
  369. {
  370. LogPrintf("BitcoinMiner started\n");
  371. SetThreadPriority(THREAD_PRIORITY_LOWEST);
  372. RenameThread("bitcoin-miner");
  373. // Each thread has its own key and counter
  374. CReserveKey reservekey(pwallet);
  375. unsigned int nExtraNonce = 0;
  376. try {
  377. while (true) {
  378. if (Params().MiningRequiresPeers()) {
  379. // Busy-wait for the network to come online so we don't waste time mining
  380. // on an obsolete chain. In regtest mode we expect to fly solo.
  381. while (vNodes.empty())
  382. MilliSleep(1000);
  383. }
  384. //
  385. // Create new block
  386. //
  387. unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
  388. CBlockIndex* pindexPrev = chainActive.Tip();
  389. auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
  390. if (!pblocktemplate.get())
  391. {
  392. LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
  393. return;
  394. }
  395. CBlock *pblock = &pblocktemplate->block;
  396. IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
  397. LogPrintf("Running BitcoinMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
  398. ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
  399. //
  400. // Search
  401. //
  402. int64_t nStart = GetTime();
  403. uint256 hashTarget = uint256().SetCompact(pblock->nBits);
  404. uint256 hash;
  405. uint32_t nNonce = 0;
  406. uint32_t nOldNonce = 0;
  407. while (true) {
  408. bool fFound = ScanHash(pblock, nNonce, &hash);
  409. uint32_t nHashesDone = nNonce - nOldNonce;
  410. nOldNonce = nNonce;
  411. // Check if something found
  412. if (fFound)
  413. {
  414. if (hash <= hashTarget)
  415. {
  416. // Found a solution
  417. pblock->nNonce = nNonce;
  418. assert(hash == pblock->GetHash());
  419. SetThreadPriority(THREAD_PRIORITY_NORMAL);
  420. LogPrintf("BitcoinMiner:\n");
  421. LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex(), hashTarget.GetHex());
  422. ProcessBlockFound(pblock, *pwallet, reservekey);
  423. SetThreadPriority(THREAD_PRIORITY_LOWEST);
  424. // In regression test mode, stop mining after a block is found.
  425. if (Params().MineBlocksOnDemand())
  426. throw boost::thread_interrupted();
  427. break;
  428. }
  429. }
  430. // Meter hashes/sec
  431. static int64_t nHashCounter;
  432. if (nHPSTimerStart == 0)
  433. {
  434. nHPSTimerStart = GetTimeMillis();
  435. nHashCounter = 0;
  436. }
  437. else
  438. nHashCounter += nHashesDone;
  439. if (GetTimeMillis() - nHPSTimerStart > 4000)
  440. {
  441. static CCriticalSection cs;
  442. {
  443. LOCK(cs);
  444. if (GetTimeMillis() - nHPSTimerStart > 4000)
  445. {
  446. dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart);
  447. nHPSTimerStart = GetTimeMillis();
  448. nHashCounter = 0;
  449. static int64_t nLogTime;
  450. if (GetTime() - nLogTime > 30 * 60)
  451. {
  452. nLogTime = GetTime();
  453. LogPrintf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0);
  454. }
  455. }
  456. }
  457. }
  458. // Check for stop or if block needs to be rebuilt
  459. boost::this_thread::interruption_point();
  460. // Regtest mode doesn't require peers
  461. if (vNodes.empty() && Params().MiningRequiresPeers())
  462. break;
  463. if (nNonce >= 0xffff0000)
  464. break;
  465. if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
  466. break;
  467. if (pindexPrev != chainActive.Tip())
  468. break;
  469. // Update nTime every few seconds
  470. UpdateTime(pblock, pindexPrev);
  471. if (Params().AllowMinDifficultyBlocks())
  472. {
  473. // Changing pblock->nTime can change work required on testnet:
  474. hashTarget.SetCompact(pblock->nBits);
  475. }
  476. }
  477. }
  478. }
  479. catch (boost::thread_interrupted)
  480. {
  481. LogPrintf("BitcoinMiner terminated\n");
  482. throw;
  483. }
  484. }
  485. void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
  486. {
  487. static boost::thread_group* minerThreads = NULL;
  488. if (nThreads < 0) {
  489. // In regtest threads defaults to 1
  490. if (Params().DefaultMinerThreads())
  491. nThreads = Params().DefaultMinerThreads();
  492. else
  493. nThreads = boost::thread::hardware_concurrency();
  494. }
  495. if (minerThreads != NULL)
  496. {
  497. minerThreads->interrupt_all();
  498. delete minerThreads;
  499. minerThreads = NULL;
  500. }
  501. if (nThreads == 0 || !fGenerate)
  502. return;
  503. minerThreads = new boost::thread_group();
  504. for (int i = 0; i < nThreads; i++)
  505. minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
  506. }
  507. #endif // ENABLE_WALLET