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

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