aboutsummaryrefslogtreecommitdiff
path: root/src/miner.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/miner.cpp')
-rw-r--r--src/miner.cpp580
1 files changed, 580 insertions, 0 deletions
diff --git a/src/miner.cpp b/src/miner.cpp
new file mode 100644
index 000000000..bb6b51337
--- /dev/null
+++ b/src/miner.cpp
@@ -0,0 +1,580 @@
+// Copyright (c) 2009-2010 Satoshi Nakamoto
+// Copyright (c) 2009-2014 The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#include "miner.h"
+
+#include "amount.h"
+#include "chain.h"
+#include "chainparams.h"
+#include "coins.h"
+#include "consensus/consensus.h"
+#include "consensus/validation.h"
+#include "hash.h"
+#include "main.h"
+#include "net.h"
+#include "policy/policy.h"
+#include "pow.h"
+#include "primitives/transaction.h"
+#include "script/standard.h"
+#include "timedata.h"
+#include "txmempool.h"
+#include "util.h"
+#include "utilmoneystr.h"
+#include "validationinterface.h"
+
+#include <boost/thread.hpp>
+#include <boost/tuple/tuple.hpp>
+
+using namespace std;
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// BitcoinMiner
+//
+
+//
+// Unconfirmed transactions in the memory pool often depend on other
+// transactions in the memory pool. When we select transactions from the
+// pool, we select by highest priority or fee rate, so we might consider
+// transactions that depend on transactions that aren't yet in the block.
+// The COrphan class keeps track of these 'temporary orphans' while
+// CreateBlock is figuring out which transactions to include.
+//
+class COrphan
+{
+public:
+ const CTransaction* ptx;
+ set<uint256> setDependsOn;
+ CFeeRate feeRate;
+ double dPriority;
+
+ COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0)
+ {
+ }
+};
+
+uint64_t nLastBlockTx = 0;
+uint64_t nLastBlockSize = 0;
+
+// We want to sort transactions by priority and fee rate, so:
+typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
+class TxPriorityCompare
+{
+ bool byFee;
+
+public:
+ TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
+
+ bool operator()(const TxPriority& a, const TxPriority& b)
+ {
+ if (byFee)
+ {
+ if (a.get<1>() == b.get<1>())
+ return a.get<0>() < b.get<0>();
+ return a.get<1>() < b.get<1>();
+ }
+ else
+ {
+ if (a.get<0>() == b.get<0>())
+ return a.get<1>() < b.get<1>();
+ return a.get<0>() < b.get<0>();
+ }
+ }
+};
+
+int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev)
+{
+ int64_t nOldTime = pblock->nTime;
+ int64_t nNewTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime());
+
+ if (nOldTime < nNewTime)
+ pblock->nTime = nNewTime;
+
+ // Updating time can change work required on testnet:
+ if (consensusParams.fPowAllowMinDifficultyBlocks)
+ pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams);
+
+ return nNewTime - nOldTime;
+}
+
+CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& scriptPubKeyIn)
+{
+ // Create new block
+ auto_ptr<CBlockTemplate> pblocktemplate(new CBlockTemplate());
+ if(!pblocktemplate.get())
+ return NULL;
+ CBlock *pblock = &pblocktemplate->block; // pointer for convenience
+
+ // -regtest only: allow overriding block.nVersion with
+ // -blockversion=N to test forking scenarios
+ if (chainparams.MineBlocksOnDemand())
+ pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
+
+ // Create coinbase tx
+ CMutableTransaction txNew;
+ txNew.vin.resize(1);
+ txNew.vin[0].prevout.SetNull();
+ txNew.vout.resize(1);
+ txNew.vout[0].scriptPubKey = scriptPubKeyIn;
+
+ // Add dummy coinbase tx as first transaction
+ pblock->vtx.push_back(CTransaction());
+ pblocktemplate->vTxFees.push_back(-1); // updated at end
+ pblocktemplate->vTxSigOps.push_back(-1); // updated at end
+
+ // Largest block you're willing to create:
+ unsigned int nBlockMaxSize = GetArg("-blockmaxsize", DEFAULT_BLOCK_MAX_SIZE);
+ // Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
+ nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize));
+
+ // How much of the block should be dedicated to high-priority transactions,
+ // included regardless of the fees they pay
+ unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", DEFAULT_BLOCK_PRIORITY_SIZE);
+ nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
+
+ // Minimum block size you want to create; block will be filled with free transactions
+ // until there are no more or the block reaches this size:
+ unsigned int nBlockMinSize = GetArg("-blockminsize", DEFAULT_BLOCK_MIN_SIZE);
+ nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
+
+ // Collect memory pool transactions into the block
+ CAmount nFees = 0;
+
+ {
+ LOCK2(cs_main, mempool.cs);
+ CBlockIndex* pindexPrev = chainActive.Tip();
+ const int nHeight = pindexPrev->nHeight + 1;
+ pblock->nTime = GetAdjustedTime();
+ const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
+ CCoinsViewCache view(pcoinsTip);
+
+ // Priority order to process transactions
+ list<COrphan> vOrphan; // list memory doesn't move
+ map<uint256, vector<COrphan*> > mapDependers;
+ bool fPrintPriority = GetBoolArg("-printpriority", false);
+
+ // This vector will be sorted into a priority queue:
+ vector<TxPriority> vecPriority;
+ vecPriority.reserve(mempool.mapTx.size());
+ for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
+ mi != mempool.mapTx.end(); ++mi)
+ {
+ const CTransaction& tx = mi->GetTx();
+
+ int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
+ ? nMedianTimePast
+ : pblock->GetBlockTime();
+
+ if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff))
+ continue;
+
+ COrphan* porphan = NULL;
+ double dPriority = 0;
+ CAmount nTotalIn = 0;
+ bool fMissingInputs = false;
+ BOOST_FOREACH(const CTxIn& txin, tx.vin)
+ {
+ // Read prev transaction
+ if (!view.HaveCoins(txin.prevout.hash))
+ {
+ // This should never happen; all transactions in the memory
+ // pool should connect to either transactions in the chain
+ // or other transactions in the memory pool.
+ if (!mempool.mapTx.count(txin.prevout.hash))
+ {
+ LogPrintf("ERROR: mempool transaction missing input\n");
+ if (fDebug) assert("mempool transaction missing input" == 0);
+ fMissingInputs = true;
+ if (porphan)
+ vOrphan.pop_back();
+ break;
+ }
+
+ // Has to wait for dependencies
+ if (!porphan)
+ {
+ // Use list for automatic deletion
+ vOrphan.push_back(COrphan(&tx));
+ porphan = &vOrphan.back();
+ }
+ mapDependers[txin.prevout.hash].push_back(porphan);
+ porphan->setDependsOn.insert(txin.prevout.hash);
+ nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
+ continue;
+ }
+ const CCoins* coins = view.AccessCoins(txin.prevout.hash);
+ assert(coins);
+
+ CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
+ nTotalIn += nValueIn;
+
+ int nConf = nHeight - coins->nHeight;
+
+ dPriority += (double)nValueIn * nConf;
+ }
+ if (fMissingInputs) continue;
+
+ // Priority is sum(valuein * age) / modified_txsize
+ unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+ dPriority = tx.ComputePriority(dPriority, nTxSize);
+
+ uint256 hash = tx.GetHash();
+ mempool.ApplyDeltas(hash, dPriority, nTotalIn);
+
+ CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
+
+ if (porphan)
+ {
+ porphan->dPriority = dPriority;
+ porphan->feeRate = feeRate;
+ }
+ else
+ vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx())));
+ }
+
+ // Collect transactions into block
+ uint64_t nBlockSize = 1000;
+ uint64_t nBlockTx = 0;
+ int nBlockSigOps = 100;
+ bool fSortedByFee = (nBlockPrioritySize <= 0);
+
+ TxPriorityCompare comparer(fSortedByFee);
+ std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
+
+ while (!vecPriority.empty())
+ {
+ // Take highest priority transaction off the priority queue:
+ double dPriority = vecPriority.front().get<0>();
+ CFeeRate feeRate = vecPriority.front().get<1>();
+ const CTransaction& tx = *(vecPriority.front().get<2>());
+
+ std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
+ vecPriority.pop_back();
+
+ // Size limits
+ unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
+ if (nBlockSize + nTxSize >= nBlockMaxSize)
+ continue;
+
+ // Legacy limits on sigOps:
+ unsigned int nTxSigOps = GetLegacySigOpCount(tx);
+ if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
+ continue;
+
+ // Skip free transactions if we're past the minimum block size:
+ const uint256& hash = tx.GetHash();
+ double dPriorityDelta = 0;
+ CAmount nFeeDelta = 0;
+ mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
+ if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
+ continue;
+
+ // Prioritise by fee once past the priority size or we run out of high-priority
+ // transactions:
+ if (!fSortedByFee &&
+ ((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
+ {
+ fSortedByFee = true;
+ comparer = TxPriorityCompare(fSortedByFee);
+ std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
+ }
+
+ if (!view.HaveInputs(tx))
+ continue;
+
+ CAmount nTxFees = view.GetValueIn(tx)-tx.GetValueOut();
+
+ nTxSigOps += GetP2SHSigOpCount(tx, view);
+ if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
+ continue;
+
+ // Note that flags: we don't want to set mempool/IsStandard()
+ // policy here, but we still have to ensure that the block we
+ // create only contains transactions that are valid in new blocks.
+ CValidationState state;
+ if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
+ continue;
+
+ UpdateCoins(tx, state, view, nHeight);
+
+ // Added
+ pblock->vtx.push_back(tx);
+ pblocktemplate->vTxFees.push_back(nTxFees);
+ pblocktemplate->vTxSigOps.push_back(nTxSigOps);
+ nBlockSize += nTxSize;
+ ++nBlockTx;
+ nBlockSigOps += nTxSigOps;
+ nFees += nTxFees;
+
+ if (fPrintPriority)
+ {
+ LogPrintf("priority %.1f fee %s txid %s\n",
+ dPriority, feeRate.ToString(), tx.GetHash().ToString());
+ }
+
+ // Add transactions that depend on this one to the priority queue
+ if (mapDependers.count(hash))
+ {
+ BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
+ {
+ if (!porphan->setDependsOn.empty())
+ {
+ porphan->setDependsOn.erase(hash);
+ if (porphan->setDependsOn.empty())
+ {
+ vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx));
+ std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
+ }
+ }
+ }
+ }
+ }
+
+ nLastBlockTx = nBlockTx;
+ nLastBlockSize = nBlockSize;
+ LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize);
+
+ // Compute final coinbase transaction.
+ txNew.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
+ txNew.vin[0].scriptSig = CScript() << nHeight << OP_0;
+ pblock->vtx[0] = txNew;
+ pblocktemplate->vTxFees[0] = -nFees;
+
+ // Fill in header
+ pblock->hashPrevBlock = pindexPrev->GetBlockHash();
+ UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
+ pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
+ pblock->nNonce = 0;
+ pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
+
+ CValidationState state;
+ if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false))
+ throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed");
+ }
+
+ return pblocktemplate.release();
+}
+
+void IncrementExtraNonce(CBlock* pblock, const CBlockIndex* pindexPrev, unsigned int& nExtraNonce)
+{
+ // Update nExtraNonce
+ static uint256 hashPrevBlock;
+ if (hashPrevBlock != pblock->hashPrevBlock)
+ {
+ nExtraNonce = 0;
+ hashPrevBlock = pblock->hashPrevBlock;
+ }
+ ++nExtraNonce;
+ unsigned int nHeight = pindexPrev->nHeight+1; // Height first in coinbase required for block.version=2
+ CMutableTransaction txCoinbase(pblock->vtx[0]);
+ txCoinbase.vin[0].scriptSig = (CScript() << nHeight << CScriptNum(nExtraNonce)) + COINBASE_FLAGS;
+ assert(txCoinbase.vin[0].scriptSig.size() <= 100);
+
+ pblock->vtx[0] = txCoinbase;
+ pblock->hashMerkleRoot = pblock->ComputeMerkleRoot();
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// Internal miner
+//
+
+//
+// ScanHash scans nonces looking for a hash with at least some zero bits.
+// The nonce is usually preserved between calls, but periodically or if the
+// nonce is 0xffff0000 or above, the block is rebuilt and nNonce starts over at
+// zero.
+//
+bool static ScanHash(const CBlockHeader *pblock, uint32_t& nNonce, uint256 *phash)
+{
+ // Write the first 76 bytes of the block header to a double-SHA256 state.
+ CHash256 hasher;
+ CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
+ ss << *pblock;
+ assert(ss.size() == 80);
+ hasher.Write((unsigned char*)&ss[0], 76);
+
+ while (true) {
+ nNonce++;
+
+ // Write the last 4 bytes of the block header (the nonce) to a copy of
+ // the double-SHA256 state, and compute the result.
+ CHash256(hasher).Write((unsigned char*)&nNonce, 4).Finalize((unsigned char*)phash);
+
+ // Return the nonce if the hash has at least some zero bits,
+ // caller will check if it has enough to reach the target
+ if (((uint16_t*)phash)[15] == 0)
+ return true;
+
+ // If nothing found after trying for a while, return -1
+ if ((nNonce & 0xfff) == 0)
+ return false;
+ }
+}
+
+static bool ProcessBlockFound(const CBlock* pblock, const CChainParams& chainparams)
+{
+ LogPrintf("%s\n", pblock->ToString());
+ LogPrintf("generated %s\n", FormatMoney(pblock->vtx[0].vout[0].nValue));
+
+ // Found a solution
+ {
+ LOCK(cs_main);
+ if (pblock->hashPrevBlock != chainActive.Tip()->GetBlockHash())
+ return error("BitcoinMiner: generated block is stale");
+ }
+
+ // Inform about the new block
+ GetMainSignals().BlockFound(pblock->GetHash());
+
+ // Process this block the same as if we had received it from another node
+ CValidationState state;
+ if (!ProcessNewBlock(state, chainparams, NULL, pblock, true, NULL))
+ return error("BitcoinMiner: ProcessNewBlock, block not accepted");
+
+ return true;
+}
+
+void static BitcoinMiner(const CChainParams& chainparams)
+{
+ LogPrintf("BitcoinMiner started\n");
+ SetThreadPriority(THREAD_PRIORITY_LOWEST);
+ RenameThread("bitcoin-miner");
+
+ unsigned int nExtraNonce = 0;
+
+ boost::shared_ptr<CReserveScript> coinbaseScript;
+ GetMainSignals().ScriptForMining(coinbaseScript);
+
+ try {
+ // Throw an error if no script was provided. This can happen
+ // due to some internal error but also if the keypool is empty.
+ // In the latter case, already the pointer is NULL.
+ if (!coinbaseScript || coinbaseScript->reserveScript.empty())
+ throw std::runtime_error("No coinbase script available (mining requires a wallet)");
+
+ while (true) {
+ if (chainparams.MiningRequiresPeers()) {
+ // Busy-wait for the network to come online so we don't waste time mining
+ // on an obsolete chain. In regtest mode we expect to fly solo.
+ do {
+ bool fvNodesEmpty;
+ {
+ LOCK(cs_vNodes);
+ fvNodesEmpty = vNodes.empty();
+ }
+ if (!fvNodesEmpty && !IsInitialBlockDownload())
+ break;
+ MilliSleep(1000);
+ } while (true);
+ }
+
+ //
+ // Create new block
+ //
+ unsigned int nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
+ CBlockIndex* pindexPrev = chainActive.Tip();
+
+ auto_ptr<CBlockTemplate> pblocktemplate(CreateNewBlock(chainparams, coinbaseScript->reserveScript));
+ if (!pblocktemplate.get())
+ {
+ LogPrintf("Error in BitcoinMiner: Keypool ran out, please call keypoolrefill before restarting the mining thread\n");
+ return;
+ }
+ CBlock *pblock = &pblocktemplate->block;
+ IncrementExtraNonce(pblock, pindexPrev, nExtraNonce);
+
+ LogPrintf("Running BitcoinMiner with %u transactions in block (%u bytes)\n", pblock->vtx.size(),
+ ::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
+
+ //
+ // Search
+ //
+ int64_t nStart = GetTime();
+ arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
+ uint256 hash;
+ uint32_t nNonce = 0;
+ while (true) {
+ // Check if something found
+ if (ScanHash(pblock, nNonce, &hash))
+ {
+ if (UintToArith256(hash) <= hashTarget)
+ {
+ // Found a solution
+ pblock->nNonce = nNonce;
+ assert(hash == pblock->GetHash());
+
+ SetThreadPriority(THREAD_PRIORITY_NORMAL);
+ LogPrintf("BitcoinMiner:\n");
+ LogPrintf("proof-of-work found \n hash: %s \ntarget: %s\n", hash.GetHex(), hashTarget.GetHex());
+ ProcessBlockFound(pblock, chainparams);
+ SetThreadPriority(THREAD_PRIORITY_LOWEST);
+ coinbaseScript->KeepScript();
+
+ // In regression test mode, stop mining after a block is found.
+ if (chainparams.MineBlocksOnDemand())
+ throw boost::thread_interrupted();
+
+ break;
+ }
+ }
+
+ // Check for stop or if block needs to be rebuilt
+ boost::this_thread::interruption_point();
+ // Regtest mode doesn't require peers
+ if (vNodes.empty() && chainparams.MiningRequiresPeers())
+ break;
+ if (nNonce >= 0xffff0000)
+ break;
+ if (mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast && GetTime() - nStart > 60)
+ break;
+ if (pindexPrev != chainActive.Tip())
+ break;
+
+ // Update nTime every few seconds
+ if (UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev) < 0)
+ break; // Recreate the block if the clock has run backwards,
+ // so that we can use the correct time.
+ if (chainparams.GetConsensus().fPowAllowMinDifficultyBlocks)
+ {
+ // Changing pblock->nTime can change work required on testnet:
+ hashTarget.SetCompact(pblock->nBits);
+ }
+ }
+ }
+ }
+ catch (const boost::thread_interrupted&)
+ {
+ LogPrintf("BitcoinMiner terminated\n");
+ throw;
+ }
+ catch (const std::runtime_error &e)
+ {
+ LogPrintf("BitcoinMiner runtime error: %s\n", e.what());
+ return;
+ }
+}
+
+void GenerateBitcoins(bool fGenerate, int nThreads, const CChainParams& chainparams)
+{
+ static boost::thread_group* minerThreads = NULL;
+
+ if (nThreads < 0)
+ nThreads = GetNumCores();
+
+ if (minerThreads != NULL)
+ {
+ minerThreads->interrupt_all();
+ delete minerThreads;
+ minerThreads = NULL;
+ }
+
+ if (nThreads == 0 || !fGenerate)
+ return;
+
+ minerThreads = new boost::thread_group();
+ for (int i = 0; i < nThreads; i++)
+ minerThreads->create_thread(boost::bind(&BitcoinMiner, boost::cref(chainparams)));
+}