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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  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. CValidationState state;
  274. if (!TestBlockValidity(state, *pblock, pindexPrev, false, false))
  275. throw std::runtime_error("CreateNewBlock() : TestBlockValidity failed");
  276. }
  277. return pblocktemplate.release();
  278. }
  279. void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
  280. {
  281. // Update nExtraNonce
  282. static uint256 hashPrevBlock;
  283. if (hashPrevBlock != pblock->hashPrevBlock)
  284. {
  285. nExtraNonce = 0;
  286. hashPrevBlock = pblock->hashPrevBlock;
  287. }
  288. ++nExtraNonce;
  289. unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
  290. CMutableTransaction txCoinbase(pblock->vtx[0]);
  291. txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
  292. assert(txCoinbase.vin[0].scriptSig.size() <= 100);
  293. pblock->vtx[0] = txCoinbase;
  294. pblock->hashMerkleRoot = pblock->BuildMerkleTree();
  295. }
  296. #ifdef ENABLE_WALLET
  297. //////////////////////////////////////////////////////////////////////////////
  298. //
  299. // Internal miner
  300. //
  301. double dHashesPerSec = 0.0;
  302. int64_t nHPSTimerStart = 0;
  303. //
  304. // ScanHash scans nonces looking for a hash with at least some zero bits.
  305. // The nonce is usually preserved between calls, but periodically or if the
  306. // nonce is 0xffff0000 or above, the block is rebuilt and nNonce starts over at
  307. // zero.
  308. //
  309. bool static ScanHash(const CBlockHeader *pblock, uint32_t& nNonce, uint256 *phash)
  310. {
  311. // Write the first 76 bytes of the block header to a double-SHA256 state.
  312. CHash256 hasher;
  313. CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
  314. ss << *pblock;
  315. assert(ss.size() == 80);
  316. hasher.Write((unsigned char*)&ss[0], 76);
  317. while (true) {
  318. nNonce++;
  319. // Write the last 4 bytes of the block header (the nonce) to a copy of
  320. // the double-SHA256 state, and compute the result.
  321. CHash256(hasher).Write((unsigned char*)&nNonce, 4).Finalize((unsigned char*)phash);
  322. // Return the nonce if the hash has at least some zero bits,
  323. // caller will check if it has enough to reach the target
  324. if (((uint16_t*)phash)[15] == 0)
  325. return true;
  326. // If nothing found after trying for a while, return -1
  327. if ((nNonce & 0xffff) == 0)
  328. return false;
  329. if ((nNonce & 0xfff) == 0)
  330. boost::this_thread::interruption_point();
  331. }
  332. }
  333. CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey)
  334. {
  335. CPubKey pubkey;
  336. if (!reservekey.GetReservedKey(pubkey))
  337. return NULL;
  338. CScript scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
  339. return CreateNewBlock(scriptPubKey);
  340. }
  341. bool ProcessBlockFound(CBlock* pblock, CWallet& wallet, CReserveKey& reservekey)
  342. {
  343. LogPrintf("%s\n", pblock->ToString());
  344. LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue));
  345. // Found a solution
  346. {
  347. LOCK(cs_main);
  348. if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
  349. return error("BitcoinMiner : generated block is stale");
  350. }
  351. // Remove key from key pool
  352. reservekey.KeepKey();
  353. // Track how many getdata requests this block gets
  354. {
  355. LOCK(wallet.cs_wallet);
  356. wallet.mapRequestCount[pblock->GetHash()] = 0;
  357. }
  358. // Process this block the same as if we had received it from another node
  359. CValidationState state;
  360. if (!ProcessNewBlock(state, NULL, pblock))
  361. return error("BitcoinMiner : ProcessNewBlock, block not accepted");
  362. return true;
  363. }
  364. void static BitcoinMiner(CWallet *pwallet)
  365. {
  366. LogPrintf("BitcoinMiner started\n");
  367. SetThreadPriority(THREAD_PRIORITY_LOWEST);
  368. RenameThread("bitcoin-miner");
  369. // Each thread has its own key and counter
  370. CReserveKey reservekey(pwallet);
  371. unsigned int nExtraNonce = 0;
  372. try {
  373. while (true) {
  374. if (Params().MiningRequiresPeers()) {
  375. // Busy-wait for the network to come online so we don't waste time mining
  376. // on an obsolete chain. In regtest mode we expect to fly solo.
  377. while (vNodes.empty())
  378. MilliSleep(1000);
  379. }
  380. //
  381. // Create new block
  382. //
  383. unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
  384. CBlockIndex* pindexPrev = chainActive.Tip();
  385. auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlockWithKey(reservekey));
  386. if (!pblocktemplate.get())
  387. {
  388. LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
  389. return;
  390. }
  391. CBlock *pblock = &pblocktemplate->block;
  392. IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
  393. LogPrintf("Running BitcoinMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
  394. ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
  395. //
  396. // Search
  397. //
  398. int64_t nStart = GetTime();
  399. uint256 hashTarget = uint256().SetCompact(pblock->nBits);
  400. uint256 hash;
  401. uint32_t nNonce = 0;
  402. uint32_t nOldNonce = 0;
  403. while (true) {
  404. bool fFound = ScanHash(pblock, nNonce, &hash);
  405. uint32_t nHashesDone = nNonce - nOldNonce;
  406. nOldNonce = nNonce;
  407. // Check if something found
  408. if (fFound)
  409. {
  410. if (hash <= hashTarget)
  411. {
  412. // Found a solution
  413. pblock->nNonce = nNonce;
  414. assert(hash == pblock->GetHash());
  415. SetThreadPriority(THREAD_PRIORITY_NORMAL);
  416. LogPrintf("BitcoinMiner:\n");
  417. LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex(), hashTarget.GetHex());
  418. ProcessBlockFound(pblock, *pwallet, reservekey);
  419. SetThreadPriority(THREAD_PRIORITY_LOWEST);
  420. // In regression test mode, stop mining after a block is found.
  421. if (Params().MineBlocksOnDemand())
  422. throw boost::thread_interrupted();
  423. break;
  424. }
  425. }
  426. // Meter hashes/sec
  427. static int64_t nHashCounter;
  428. if (nHPSTimerStart == 0)
  429. {
  430. nHPSTimerStart = GetTimeMillis();
  431. nHashCounter = 0;
  432. }
  433. else
  434. nHashCounter += nHashesDone;
  435. if (GetTimeMillis() - nHPSTimerStart > 4000)
  436. {
  437. static CCriticalSection cs;
  438. {
  439. LOCK(cs);
  440. if (GetTimeMillis() - nHPSTimerStart > 4000)
  441. {
  442. dHashesPerSec = 1000.0 * nHashCounter / (GetTimeMillis() - nHPSTimerStart);
  443. nHPSTimerStart = GetTimeMillis();
  444. nHashCounter = 0;
  445. static int64_t nLogTime;
  446. if (GetTime() - nLogTime > 30 * 60)
  447. {
  448. nLogTime = GetTime();
  449. LogPrintf("hashmeter %6.0f khash/s\n", dHashesPerSec/1000.0);
  450. }
  451. }
  452. }
  453. }
  454. // Check for stop or if block needs to be rebuilt
  455. boost::this_thread::interruption_point();
  456. // Regtest mode doesn't require peers
  457. if (vNodes.empty() && Params().MiningRequiresPeers())
  458. break;
  459. if (nNonce >= 0xffff0000)
  460. break;
  461. if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
  462. break;
  463. if (pindexPrev != chainActive.Tip())
  464. break;
  465. // Update nTime every few seconds
  466. UpdateTime(pblock, pindexPrev);
  467. if (Params().AllowMinDifficultyBlocks())
  468. {
  469. // Changing pblock->nTime can change work required on testnet:
  470. hashTarget.SetCompact(pblock->nBits);
  471. }
  472. }
  473. }
  474. }
  475. catch (boost::thread_interrupted)
  476. {
  477. LogPrintf("BitcoinMiner terminated\n");
  478. throw;
  479. }
  480. }
  481. void GenerateBitcoins(bool fGenerate, CWallet* pwallet, int nThreads)
  482. {
  483. static boost::thread_group* minerThreads = NULL;
  484. if (nThreads < 0) {
  485. // In regtest threads defaults to 1
  486. if (Params().DefaultMinerThreads())
  487. nThreads = Params().DefaultMinerThreads();
  488. else
  489. nThreads = boost::thread::hardware_concurrency();
  490. }
  491. if (minerThreads != NULL)
  492. {
  493. minerThreads->interrupt_all();
  494. delete minerThreads;
  495. minerThreads = NULL;
  496. }
  497. if (nThreads == 0 || !fGenerate)
  498. return;
  499. minerThreads = new boost::thread_group();
  500. for (int i = 0; i < nThreads; i++)
  501. minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet));
  502. }
  503. #endif // ENABLE_WALLET