Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14864682
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
78 KB
Subscribers
None
View Options
diff --git a/src/miner.cpp b/src/miner.cpp
index e3d490693..612bafa7f 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -1,660 +1,662 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 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 "config.h"
#include "consensus/consensus.h"
#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "hash.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 "validation.h"
#include "validationinterface.h"
#include <algorithm>
#include <queue>
#include <utility>
#include <boost/thread.hpp>
#include <boost/tuple/tuple.hpp>
static const int MAX_COINBASE_SCRIPTSIG_SIZE = 100;
//////////////////////////////////////////////////////////////////////////////
//
// 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.
uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0;
class ScoreCompare {
public:
ScoreCompare() {}
bool operator()(const CTxMemPool::txiter a, const CTxMemPool::txiter b) {
// Convert to less than.
return CompareTxMemPoolEntryByScore()(*b, *a);
}
};
-int64_t UpdateTime(CBlockHeader *pblock,
- const Consensus::Params &consensusParams,
+int64_t UpdateTime(CBlockHeader *pblock, const Config &config,
const CBlockIndex *pindexPrev) {
int64_t nOldTime = pblock->nTime;
int64_t nNewTime =
std::max(pindexPrev->GetMedianTimePast() + 1, GetAdjustedTime());
if (nOldTime < nNewTime) {
pblock->nTime = nNewTime;
}
+ const Consensus::Params &consensusParams =
+ config.GetChainParams().GetConsensus();
+
// Updating time can change work required on testnet:
if (consensusParams.fPowAllowMinDifficultyBlocks) {
pblock->nBits =
GetNextWorkRequired(pindexPrev, pblock, consensusParams);
}
return nNewTime - nOldTime;
}
static uint64_t ComputeMaxGeneratedBlockSize(const Config &config,
const CBlockIndex *pindexPrev) {
// Block resource limits
// If -blockmaxsize is not given, limit to DEFAULT_MAX_GENERATED_BLOCK_SIZE
// If only one is given, only restrict the specified resource.
// If both are given, restrict both.
uint64_t nMaxGeneratedBlockSize = DEFAULT_MAX_GENERATED_BLOCK_SIZE;
if (IsArgSet("-blockmaxsize")) {
nMaxGeneratedBlockSize =
GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE);
}
// Limit size to between 1K and MaxBlockSize-1K for sanity:
nMaxGeneratedBlockSize =
std::max(uint64_t(1000), std::min(config.GetMaxBlockSize() - 1000,
nMaxGeneratedBlockSize));
return nMaxGeneratedBlockSize;
}
BlockAssembler::BlockAssembler(const Config &_config,
const CChainParams &_chainparams)
: chainparams(_chainparams), config(&_config) {
if (IsArgSet("-blockmintxfee")) {
Amount n(0);
ParseMoney(GetArg("-blockmintxfee", ""), n);
blockMinFeeRate = CFeeRate(n);
} else {
blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE);
}
LOCK(cs_main);
nMaxGeneratedBlockSize =
ComputeMaxGeneratedBlockSize(*config, chainActive.Tip());
}
void BlockAssembler::resetBlock() {
inBlock.clear();
// Reserve space for coinbase tx.
nBlockSize = 1000;
nBlockSigOps = 100;
// These counters do not include coinbase tx.
nBlockTx = 0;
nFees = Amount(0);
lastFewTxs = 0;
blockFinished = false;
}
static const std::vector<uint8_t>
getExcessiveBlockSizeSig(const Config &config) {
std::string cbmsg = "/EB" + getSubVersionEB(config.GetMaxBlockSize()) + "/";
const char *cbcstr = cbmsg.c_str();
std::vector<uint8_t> vec(cbcstr, cbcstr + cbmsg.size());
return vec;
}
std::unique_ptr<CBlockTemplate>
BlockAssembler::CreateNewBlock(const CScript &scriptPubKeyIn) {
int64_t nTimeStart = GetTimeMicros();
resetBlock();
pblocktemplate.reset(new CBlockTemplate());
if (!pblocktemplate.get()) {
return nullptr;
}
// Pointer for convenience.
pblock = &pblocktemplate->block;
// Add dummy coinbase tx as first transaction.
pblock->vtx.emplace_back();
// updated at end
pblocktemplate->vTxFees.push_back(Amount(-1));
// updated at end
pblocktemplate->vTxSigOpsCount.push_back(-1);
LOCK2(cs_main, mempool.cs);
CBlockIndex *pindexPrev = chainActive.Tip();
nHeight = pindexPrev->nHeight + 1;
pblock->nVersion =
ComputeBlockVersion(pindexPrev, chainparams.GetConsensus());
// -regtest only: allow overriding block.nVersion with
// -blockversion=N to test forking scenarios
if (chainparams.MineBlocksOnDemand()) {
pblock->nVersion = GetArg("-blockversion", pblock->nVersion);
}
pblock->nTime = GetAdjustedTime();
nMaxGeneratedBlockSize = ComputeMaxGeneratedBlockSize(*config, pindexPrev);
nLockTimeCutoff =
(STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
? pindexPrev->GetMedianTimePast()
: pblock->GetBlockTime();
addPriorityTxs();
int nPackagesSelected = 0;
int nDescendantsUpdated = 0;
addPackageTxs(nPackagesSelected, nDescendantsUpdated);
int64_t nTime1 = GetTimeMicros();
nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize;
// Create coinbase transaction.
CMutableTransaction coinbaseTx;
coinbaseTx.vin.resize(1);
coinbaseTx.vin[0].prevout.SetNull();
coinbaseTx.vout.resize(1);
coinbaseTx.vout[0].scriptPubKey = scriptPubKeyIn;
coinbaseTx.vout[0].nValue =
nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
coinbaseTx.vin[0].scriptSig = CScript() << nHeight << OP_0;
pblock->vtx[0] = MakeTransactionRef(coinbaseTx);
pblocktemplate->vTxFees[0] = -1 * nFees;
uint64_t nSerializeSize =
GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION);
LogPrintf("CreateNewBlock(): total size: %u txs: %u fees: %ld sigops %d\n",
nSerializeSize, nBlockTx, nFees, nBlockSigOps);
// Fill in header.
pblock->hashPrevBlock = pindexPrev->GetBlockHash();
- UpdateTime(pblock, chainparams.GetConsensus(), pindexPrev);
+ UpdateTime(pblock, *config, pindexPrev);
pblock->nBits =
GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nNonce = 0;
pblocktemplate->vTxSigOpsCount[0] =
GetSigOpCountWithoutP2SH(*pblock->vtx[0]);
CValidationState state;
if (!TestBlockValidity(*config, state, *pblock, pindexPrev, false, false)) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s",
__func__,
FormatStateMessage(state)));
}
int64_t nTime2 = GetTimeMicros();
LogPrint("bench", "CreateNewBlock() packages: %.2fms (%d packages, %d "
"updated descendants), validity: %.2fms (total %.2fms)\n",
0.001 * (nTime1 - nTimeStart), nPackagesSelected,
nDescendantsUpdated, 0.001 * (nTime2 - nTime1),
0.001 * (nTime2 - nTimeStart));
return std::move(pblocktemplate);
}
bool BlockAssembler::isStillDependent(CTxMemPool::txiter iter) {
for (CTxMemPool::txiter parent : mempool.GetMemPoolParents(iter)) {
if (!inBlock.count(parent)) {
return true;
}
}
return false;
}
void BlockAssembler::onlyUnconfirmed(CTxMemPool::setEntries &testSet) {
for (CTxMemPool::setEntries::iterator iit = testSet.begin();
iit != testSet.end();) {
// Only test txs not already in the block.
if (inBlock.count(*iit)) {
testSet.erase(iit++);
} else {
iit++;
}
}
}
bool BlockAssembler::TestPackage(uint64_t packageSize, int64_t packageSigOps) {
auto blockSizeWithPackage = nBlockSize + packageSize;
if (blockSizeWithPackage >= nMaxGeneratedBlockSize) {
return false;
}
if (nBlockSigOps + packageSigOps >=
GetMaxBlockSigOpsCount(blockSizeWithPackage)) {
return false;
}
return true;
}
// Perform transaction-level checks before adding to block:
// - transaction finality (locktime)
// - serialized size (in case -blockmaxsize is in use)
bool BlockAssembler::TestPackageTransactions(
const CTxMemPool::setEntries &package) {
uint64_t nPotentialBlockSize = nBlockSize;
for (const CTxMemPool::txiter it : package) {
CValidationState state;
if (!ContextualCheckTransaction(*config, it->GetTx(), state, nHeight,
nLockTimeCutoff)) {
return false;
}
uint64_t nTxSize =
::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION);
if (nPotentialBlockSize + nTxSize >= nMaxGeneratedBlockSize) {
return false;
}
nPotentialBlockSize += nTxSize;
}
return true;
}
bool BlockAssembler::TestForBlock(CTxMemPool::txiter it) {
auto blockSizeWithTx =
nBlockSize +
::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION);
if (blockSizeWithTx >= nMaxGeneratedBlockSize) {
if (nBlockSize > nMaxGeneratedBlockSize - 100 || lastFewTxs > 50) {
blockFinished = true;
return false;
}
if (nBlockSize > nMaxGeneratedBlockSize - 1000) {
lastFewTxs++;
}
return false;
}
auto maxBlockSigOps = GetMaxBlockSigOpsCount(blockSizeWithTx);
if (nBlockSigOps + it->GetSigOpCount() >= maxBlockSigOps) {
// If the block has room for no more sig ops then flag that the block is
// finished.
// TODO: We should consider adding another transaction that isn't very
// dense in sigops instead of bailing out so easily.
if (nBlockSigOps > maxBlockSigOps - 2) {
blockFinished = true;
return false;
}
// Otherwise attempt to find another tx with fewer sigops to put in the
// block.
return false;
}
// Must check that lock times are still valid. This can be removed once MTP
// is always enforced as long as reorgs keep the mempool consistent.
CValidationState state;
if (!ContextualCheckTransaction(*config, it->GetTx(), state, nHeight,
nLockTimeCutoff)) {
return false;
}
return true;
}
void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) {
pblock->vtx.emplace_back(iter->GetSharedTx());
pblocktemplate->vTxFees.push_back(iter->GetFee());
pblocktemplate->vTxSigOpsCount.push_back(iter->GetSigOpCount());
nBlockSize += iter->GetTxSize();
++nBlockTx;
nBlockSigOps += iter->GetSigOpCount();
nFees += iter->GetFee();
inBlock.insert(iter);
bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
if (fPrintPriority) {
double dPriority = iter->GetPriority(nHeight);
Amount dummy;
mempool.ApplyDeltas(iter->GetTx().GetId(), dPriority, dummy);
LogPrintf(
"priority %.1f fee %s txid %s\n", dPriority,
CFeeRate(iter->GetModifiedFee(), iter->GetTxSize()).ToString(),
iter->GetTx().GetId().ToString());
}
}
int BlockAssembler::UpdatePackagesForAdded(
const CTxMemPool::setEntries &alreadyAdded,
indexed_modified_transaction_set &mapModifiedTx) {
int nDescendantsUpdated = 0;
for (const CTxMemPool::txiter it : alreadyAdded) {
CTxMemPool::setEntries descendants;
mempool.CalculateDescendants(it, descendants);
// Insert all descendants (not yet in block) into the modified set.
for (CTxMemPool::txiter desc : descendants) {
if (alreadyAdded.count(desc)) {
continue;
}
++nDescendantsUpdated;
modtxiter mit = mapModifiedTx.find(desc);
if (mit == mapModifiedTx.end()) {
CTxMemPoolModifiedEntry modEntry(desc);
modEntry.nSizeWithAncestors -= it->GetTxSize();
modEntry.nModFeesWithAncestors -= it->GetModifiedFee();
modEntry.nSigOpCountWithAncestors -= it->GetSigOpCount();
mapModifiedTx.insert(modEntry);
} else {
mapModifiedTx.modify(mit, update_for_parent_inclusion(it));
}
}
}
return nDescendantsUpdated;
}
// Skip entries in mapTx that are already in a block or are present in
// mapModifiedTx (which implies that the mapTx ancestor state is stale due to
// ancestor inclusion in the block). Also skip transactions that we've already
// failed to add. This can happen if we consider a transaction in mapModifiedTx
// and it fails: we can then potentially consider it again while walking mapTx.
// It's currently guaranteed to fail again, but as a belt-and-suspenders check
// we put it in failedTx and avoid re-evaluation, since the re-evaluation would
// be using cached size/sigops/fee values that are not actually correct.
bool BlockAssembler::SkipMapTxEntry(
CTxMemPool::txiter it, indexed_modified_transaction_set &mapModifiedTx,
CTxMemPool::setEntries &failedTx) {
assert(it != mempool.mapTx.end());
if (mapModifiedTx.count(it) || inBlock.count(it) || failedTx.count(it)) {
return true;
}
return false;
}
void BlockAssembler::SortForBlock(
const CTxMemPool::setEntries &package, CTxMemPool::txiter entry,
std::vector<CTxMemPool::txiter> &sortedEntries) {
// Sort package by ancestor count. If a transaction A depends on transaction
// B, then A's ancestor count must be greater than B's. So this is
// sufficient to validly order the transactions for block inclusion.
sortedEntries.clear();
sortedEntries.insert(sortedEntries.begin(), package.begin(), package.end());
std::sort(sortedEntries.begin(), sortedEntries.end(),
CompareTxIterByAncestorCount());
}
// This transaction selection algorithm orders the mempool based on feerate of a
// transaction including all unconfirmed ancestors. Since we don't remove
// transactions from the mempool as we select them for block inclusion, we need
// an alternate method of updating the feerate of a transaction with its
// not-yet-selected ancestors as we go. This is accomplished by walking the
// in-mempool descendants of selected transactions and storing a temporary
// modified state in mapModifiedTxs. Each time through the loop, we compare the
// best transaction in mapModifiedTxs with the next transaction in the mempool
// to decide what transaction package to work on next.
void BlockAssembler::addPackageTxs(int &nPackagesSelected,
int &nDescendantsUpdated) {
// mapModifiedTx will store sorted packages after they are modified because
// some of their txs are already in the block.
indexed_modified_transaction_set mapModifiedTx;
// Keep track of entries that failed inclusion, to avoid duplicate work.
CTxMemPool::setEntries failedTx;
// Start by adding all descendants of previously added txs to mapModifiedTx
// and modifying them for their already included ancestors.
UpdatePackagesForAdded(inBlock, mapModifiedTx);
CTxMemPool::indexed_transaction_set::index<ancestor_score>::type::iterator
mi = mempool.mapTx.get<ancestor_score>().begin();
CTxMemPool::txiter iter;
// Limit the number of attempts to add transactions to the block when it is
// close to full; this is just a simple heuristic to finish quickly if the
// mempool has a lot of entries.
const int64_t MAX_CONSECUTIVE_FAILURES = 1000;
int64_t nConsecutiveFailed = 0;
while (mi != mempool.mapTx.get<ancestor_score>().end() ||
!mapModifiedTx.empty()) {
// First try to find a new transaction in mapTx to evaluate.
if (mi != mempool.mapTx.get<ancestor_score>().end() &&
SkipMapTxEntry(mempool.mapTx.project<0>(mi), mapModifiedTx,
failedTx)) {
++mi;
continue;
}
// Now that mi is not stale, determine which transaction to evaluate:
// the next entry from mapTx, or the best from mapModifiedTx?
bool fUsingModified = false;
modtxscoreiter modit = mapModifiedTx.get<ancestor_score>().begin();
if (mi == mempool.mapTx.get<ancestor_score>().end()) {
// We're out of entries in mapTx; use the entry from mapModifiedTx
iter = modit->iter;
fUsingModified = true;
} else {
// Try to compare the mapTx entry to the mapModifiedTx entry.
iter = mempool.mapTx.project<0>(mi);
if (modit != mapModifiedTx.get<ancestor_score>().end() &&
CompareModifiedEntry()(*modit, CTxMemPoolModifiedEntry(iter))) {
// The best entry in mapModifiedTx has higher score than the one
// from mapTx. Switch which transaction (package) to consider
iter = modit->iter;
fUsingModified = true;
} else {
// Either no entry in mapModifiedTx, or it's worse than mapTx.
// Increment mi for the next loop iteration.
++mi;
}
}
// We skip mapTx entries that are inBlock, and mapModifiedTx shouldn't
// contain anything that is inBlock.
assert(!inBlock.count(iter));
uint64_t packageSize = iter->GetSizeWithAncestors();
Amount packageFees = iter->GetModFeesWithAncestors();
int64_t packageSigOps = iter->GetSigOpCountWithAncestors();
if (fUsingModified) {
packageSize = modit->nSizeWithAncestors;
packageFees = modit->nModFeesWithAncestors;
packageSigOps = modit->nSigOpCountWithAncestors;
}
if (packageFees < blockMinFeeRate.GetFee(packageSize)) {
// Everything else we might consider has a lower fee rate
return;
}
if (!TestPackage(packageSize, packageSigOps)) {
if (fUsingModified) {
// Since we always look at the best entry in mapModifiedTx, we
// must erase failed entries so that we can consider the next
// best entry on the next loop iteration
mapModifiedTx.get<ancestor_score>().erase(modit);
failedTx.insert(iter);
}
++nConsecutiveFailed;
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES &&
nBlockSize > nMaxGeneratedBlockSize - 1000) {
// Give up if we're close to full and haven't succeeded in a
// while.
break;
}
continue;
}
CTxMemPool::setEntries ancestors;
uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
std::string dummy;
mempool.CalculateMemPoolAncestors(*iter, ancestors, nNoLimit, nNoLimit,
nNoLimit, nNoLimit, dummy, false);
onlyUnconfirmed(ancestors);
ancestors.insert(iter);
// Test if all tx's are Final.
if (!TestPackageTransactions(ancestors)) {
if (fUsingModified) {
mapModifiedTx.get<ancestor_score>().erase(modit);
failedTx.insert(iter);
}
continue;
}
// This transaction will make it in; reset the failed counter.
nConsecutiveFailed = 0;
// Package can be added. Sort the entries in a valid order.
std::vector<CTxMemPool::txiter> sortedEntries;
SortForBlock(ancestors, iter, sortedEntries);
for (size_t i = 0; i < sortedEntries.size(); ++i) {
AddToBlock(sortedEntries[i]);
// Erase from the modified set, if present
mapModifiedTx.erase(sortedEntries[i]);
}
++nPackagesSelected;
// Update transactions that depend on each of these
nDescendantsUpdated += UpdatePackagesForAdded(ancestors, mapModifiedTx);
}
}
void BlockAssembler::addPriorityTxs() {
// How much of the block should be dedicated to high-priority transactions,
// included regardless of the fees they pay.
if (config->GetBlockPriorityPercentage() == 0) {
return;
}
uint64_t nBlockPrioritySize =
nMaxGeneratedBlockSize * config->GetBlockPriorityPercentage() / 100;
// This vector will be sorted into a priority queue:
std::vector<TxCoinAgePriority> vecPriority;
TxCoinAgePriorityCompare pricomparer;
std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash>
waitPriMap;
typedef std::map<CTxMemPool::txiter, double,
CTxMemPool::CompareIteratorByHash>::iterator waitPriIter;
double actualPriority = -1;
vecPriority.reserve(mempool.mapTx.size());
for (CTxMemPool::indexed_transaction_set::iterator mi =
mempool.mapTx.begin();
mi != mempool.mapTx.end(); ++mi) {
double dPriority = mi->GetPriority(nHeight);
Amount dummy;
mempool.ApplyDeltas(mi->GetTx().GetId(), dPriority, dummy);
vecPriority.push_back(TxCoinAgePriority(dPriority, mi));
}
std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
CTxMemPool::txiter iter;
// Add a tx from priority queue to fill the part of block reserved to
// priority transactions.
while (!vecPriority.empty() && !blockFinished) {
iter = vecPriority.front().second;
actualPriority = vecPriority.front().first;
std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
vecPriority.pop_back();
// If tx already in block, skip.
if (inBlock.count(iter)) {
// Shouldn't happen for priority txs.
assert(false);
continue;
}
// If tx is dependent on other mempool txs which haven't yet been
// included then put it in the waitSet.
if (isStillDependent(iter)) {
waitPriMap.insert(std::make_pair(iter, actualPriority));
continue;
}
// If this tx fits in the block add it, otherwise keep looping.
if (TestForBlock(iter)) {
AddToBlock(iter);
// If now that this txs is added we've surpassed our desired
// priority size or have dropped below the AllowFreeThreshold, then
// we're done adding priority txs.
if (nBlockSize >= nBlockPrioritySize ||
!AllowFree(actualPriority)) {
break;
}
// This tx was successfully added, so add transactions that depend
// on this one to the priority queue to try again.
for (CTxMemPool::txiter child : mempool.GetMemPoolChildren(iter)) {
waitPriIter wpiter = waitPriMap.find(child);
if (wpiter != waitPriMap.end()) {
vecPriority.push_back(
TxCoinAgePriority(wpiter->second, child));
std::push_heap(vecPriority.begin(), vecPriority.end(),
pricomparer);
waitPriMap.erase(wpiter);
}
}
}
}
}
void IncrementExtraNonce(const Config &config, CBlock *pblock,
const CBlockIndex *pindexPrev,
unsigned int &nExtraNonce) {
// Update nExtraNonce
static uint256 hashPrevBlock;
if (hashPrevBlock != pblock->hashPrevBlock) {
nExtraNonce = 0;
hashPrevBlock = pblock->hashPrevBlock;
}
++nExtraNonce;
// Height first in coinbase required for block.version=2
unsigned int nHeight = pindexPrev->nHeight + 1;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.vin[0].scriptSig =
(CScript() << nHeight << CScriptNum(nExtraNonce)
<< getExcessiveBlockSizeSig(config)) +
COINBASE_FLAGS;
assert(txCoinbase.vin[0].scriptSig.size() <= MAX_COINBASE_SCRIPTSIG_SIZE);
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
}
diff --git a/src/miner.h b/src/miner.h
index 7d9bd12a8..9b2018d6b 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -1,223 +1,222 @@
// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_MINER_H
#define BITCOIN_MINER_H
#include "primitives/block.h"
#include "txmempool.h"
#include "boost/multi_index/ordered_index.hpp"
#include "boost/multi_index_container.hpp"
#include <cstdint>
#include <memory>
class CBlockIndex;
class CChainParams;
class Config;
class CReserveKey;
class CScript;
class CWallet;
namespace Consensus {
struct Params;
};
static const bool DEFAULT_PRINTPRIORITY = false;
struct CBlockTemplate {
CBlock block;
std::vector<Amount> vTxFees;
std::vector<int64_t> vTxSigOpsCount;
};
// Container for tracking updates to ancestor feerate as we include (parent)
// transactions in a block
struct CTxMemPoolModifiedEntry {
CTxMemPoolModifiedEntry(CTxMemPool::txiter entry) {
iter = entry;
nSizeWithAncestors = entry->GetSizeWithAncestors();
nModFeesWithAncestors = entry->GetModFeesWithAncestors();
nSigOpCountWithAncestors = entry->GetSigOpCountWithAncestors();
}
CTxMemPool::txiter iter;
uint64_t nSizeWithAncestors;
Amount nModFeesWithAncestors;
int64_t nSigOpCountWithAncestors;
};
/**
* Comparator for CTxMemPool::txiter objects.
* It simply compares the internal memory address of the CTxMemPoolEntry object
* pointed to. This means it has no meaning, and is only useful for using them
* as key in other indexes.
*/
struct CompareCTxMemPoolIter {
bool operator()(const CTxMemPool::txiter &a,
const CTxMemPool::txiter &b) const {
return &(*a) < &(*b);
}
};
struct modifiedentry_iter {
typedef CTxMemPool::txiter result_type;
result_type operator()(const CTxMemPoolModifiedEntry &entry) const {
return entry.iter;
}
};
// This matches the calculation in CompareTxMemPoolEntryByAncestorFee,
// except operating on CTxMemPoolModifiedEntry.
// TODO: refactor to avoid duplication of this logic.
struct CompareModifiedEntry {
bool operator()(const CTxMemPoolModifiedEntry &a,
const CTxMemPoolModifiedEntry &b) {
double f1 = double(b.nSizeWithAncestors *
a.nModFeesWithAncestors.GetSatoshis());
double f2 = double(a.nSizeWithAncestors *
b.nModFeesWithAncestors.GetSatoshis());
if (f1 == f2) {
return CTxMemPool::CompareIteratorByHash()(a.iter, b.iter);
}
return f1 > f2;
}
};
// A comparator that sorts transactions based on number of ancestors.
// This is sufficient to sort an ancestor package in an order that is valid
// to appear in a block.
struct CompareTxIterByAncestorCount {
bool operator()(const CTxMemPool::txiter &a, const CTxMemPool::txiter &b) {
if (a->GetCountWithAncestors() != b->GetCountWithAncestors())
return a->GetCountWithAncestors() < b->GetCountWithAncestors();
return CTxMemPool::CompareIteratorByHash()(a, b);
}
};
typedef boost::multi_index_container<
CTxMemPoolModifiedEntry,
boost::multi_index::indexed_by<
boost::multi_index::ordered_unique<modifiedentry_iter,
CompareCTxMemPoolIter>,
// sorted by modified ancestor fee rate
boost::multi_index::ordered_non_unique<
// Reuse same tag from CTxMemPool's similar index
boost::multi_index::tag<ancestor_score>,
boost::multi_index::identity<CTxMemPoolModifiedEntry>,
CompareModifiedEntry>>>
indexed_modified_transaction_set;
typedef indexed_modified_transaction_set::nth_index<0>::type::iterator
modtxiter;
typedef indexed_modified_transaction_set::index<ancestor_score>::type::iterator
modtxscoreiter;
struct update_for_parent_inclusion {
update_for_parent_inclusion(CTxMemPool::txiter it) : iter(it) {}
void operator()(CTxMemPoolModifiedEntry &e) {
e.nModFeesWithAncestors -= iter->GetFee();
e.nSizeWithAncestors -= iter->GetTxSize();
e.nSigOpCountWithAncestors -= iter->GetSigOpCount();
}
CTxMemPool::txiter iter;
};
/** Generate a new block, without valid proof-of-work */
class BlockAssembler {
private:
// The constructed block template
std::unique_ptr<CBlockTemplate> pblocktemplate;
// A convenience pointer that always refers to the CBlock in pblocktemplate
CBlock *pblock;
// Configuration parameters for the block size
uint64_t nMaxGeneratedBlockSize;
CFeeRate blockMinFeeRate;
// Information on the current status of the block
uint64_t nBlockSize;
uint64_t nBlockTx;
uint64_t nBlockSigOps;
Amount nFees;
CTxMemPool::setEntries inBlock;
// Chain context for the block
int nHeight;
int64_t nLockTimeCutoff;
const CChainParams &chainparams;
const Config *config;
// Variables used for addPriorityTxs
int lastFewTxs;
bool blockFinished;
public:
BlockAssembler(const Config &_config, const CChainParams &chainparams);
/** Construct a new block template with coinbase to scriptPubKeyIn */
std::unique_ptr<CBlockTemplate>
CreateNewBlock(const CScript &scriptPubKeyIn);
uint64_t GetMaxGeneratedBlockSize() const { return nMaxGeneratedBlockSize; }
private:
// utility functions
/** Clear the block's state and prepare for assembling a new block */
void resetBlock();
/** Add a tx to the block */
void AddToBlock(CTxMemPool::txiter iter);
// Methods for how to add transactions to a block.
/** Add transactions based on tx "priority" */
void addPriorityTxs();
/** Add transactions based on feerate including unconfirmed ancestors
* Increments nPackagesSelected / nDescendantsUpdated with corresponding
* statistics from the package selection (for logging statistics). */
void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated);
// helper function for addPriorityTxs
/** Test if tx will still "fit" in the block */
bool TestForBlock(CTxMemPool::txiter iter);
/** Test if tx still has unconfirmed parents not yet in block */
bool isStillDependent(CTxMemPool::txiter iter);
// helper functions for addPackageTxs()
/** Remove confirmed (inBlock) entries from given set */
void onlyUnconfirmed(CTxMemPool::setEntries &testSet);
/** Test if a new package would "fit" in the block */
bool TestPackage(uint64_t packageSize, int64_t packageSigOpsCost);
/** Perform checks on each transaction in a package:
* locktime, serialized size (if necessary)
* These checks should always succeed, and they're here
* only as an extra check in case of suboptimal node configuration */
bool TestPackageTransactions(const CTxMemPool::setEntries &package);
/** Return true if given transaction from mapTx has already been evaluated,
* or if the transaction's cached data in mapTx is incorrect. */
bool SkipMapTxEntry(CTxMemPool::txiter it,
indexed_modified_transaction_set &mapModifiedTx,
CTxMemPool::setEntries &failedTx);
/** Sort the package in an order that is valid to appear in a block */
void SortForBlock(const CTxMemPool::setEntries &package,
CTxMemPool::txiter entry,
std::vector<CTxMemPool::txiter> &sortedEntries);
/** Add descendants of given transactions to mapModifiedTx with ancestor
* state updated assuming given transactions are inBlock. Returns number
* of updated descendants. */
int UpdatePackagesForAdded(const CTxMemPool::setEntries &alreadyAdded,
indexed_modified_transaction_set &mapModifiedTx);
};
/** Modify the extranonce in a block */
void IncrementExtraNonce(const Config &config, CBlock *pblock,
const CBlockIndex *pindexPrev,
unsigned int &nExtraNonce);
-int64_t UpdateTime(CBlockHeader *pblock,
- const Consensus::Params &consensusParams,
+int64_t UpdateTime(CBlockHeader *pblock, const Config &config,
const CBlockIndex *pindexPrev);
#endif // BITCOIN_MINER_H
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 01ef17edd..8754c2783 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -1,1097 +1,1097 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 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 "amount.h"
#include "chain.h"
#include "chainparams.h"
#include "config.h"
#include "consensus/consensus.h"
#include "consensus/params.h"
#include "consensus/validation.h"
#include "core_io.h"
#include "dstencode.h"
#include "init.h"
#include "miner.h"
#include "net.h"
#include "policy/policy.h"
#include "pow.h"
#include "rpc/blockchain.h"
#include "rpc/server.h"
#include "txmempool.h"
#include "util.h"
#include "utilstrencodings.h"
#include "validation.h"
#include "validationinterface.h"
#include <univalue.h>
#include <cstdint>
#include <memory>
/**
* Return average network hashes per second based on the last 'lookup' blocks,
* or from the last difficulty change if 'lookup' is nonpositive. If 'height' is
* nonnegative, compute the estimate at the time when a given block was found.
*/
static UniValue GetNetworkHashPS(int lookup, int height) {
CBlockIndex *pb = chainActive.Tip();
if (height >= 0 && height < chainActive.Height()) {
pb = chainActive[height];
}
if (pb == nullptr || !pb->nHeight) {
return 0;
}
// If lookup is -1, then use blocks since last difficulty change.
if (lookup <= 0) {
lookup = pb->nHeight %
Params().GetConsensus().DifficultyAdjustmentInterval() +
1;
}
// If lookup is larger than chain, then set it to chain length.
if (lookup > pb->nHeight) {
lookup = pb->nHeight;
}
CBlockIndex *pb0 = pb;
int64_t minTime = pb0->GetBlockTime();
int64_t maxTime = minTime;
for (int i = 0; i < lookup; i++) {
pb0 = pb0->pprev;
int64_t time = pb0->GetBlockTime();
minTime = std::min(time, minTime);
maxTime = std::max(time, maxTime);
}
// In case there's a situation where minTime == maxTime, we don't want a
// divide by zero exception.
if (minTime == maxTime) {
return 0;
}
arith_uint256 workDiff = pb->nChainWork - pb0->nChainWork;
int64_t timeDiff = maxTime - minTime;
return workDiff.getdouble() / timeDiff;
}
static UniValue getnetworkhashps(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 2) {
throw std::runtime_error(
"getnetworkhashps ( nblocks height )\n"
"\nReturns the estimated network hashes per second based on the "
"last n blocks.\n"
"Pass in [blocks] to override # of blocks, -1 specifies since last "
"difficulty change.\n"
"Pass in [height] to estimate the network speed at the time when a "
"certain block was found.\n"
"\nArguments:\n"
"1. nblocks (numeric, optional, default=120) The number of "
"blocks, or -1 for blocks since last difficulty change.\n"
"2. height (numeric, optional, default=-1) To estimate at the "
"time of the given height.\n"
"\nResult:\n"
"x (numeric) Hashes per second estimated\n"
"\nExamples:\n" +
HelpExampleCli("getnetworkhashps", "") +
HelpExampleRpc("getnetworkhashps", ""));
}
LOCK(cs_main);
return GetNetworkHashPS(
request.params.size() > 0 ? request.params[0].get_int() : 120,
request.params.size() > 1 ? request.params[1].get_int() : -1);
}
static UniValue generateBlocks(const Config &config,
std::shared_ptr<CReserveScript> coinbaseScript,
int nGenerate, uint64_t nMaxTries,
bool keepScript) {
static const int nInnerLoopCount = 0x100000;
int nHeightStart = 0;
int nHeightEnd = 0;
int nHeight = 0;
{
// Don't keep cs_main locked.
LOCK(cs_main);
nHeightStart = chainActive.Height();
nHeight = nHeightStart;
nHeightEnd = nHeightStart + nGenerate;
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd) {
std::unique_ptr<CBlockTemplate> pblocktemplate(
BlockAssembler(config, Params())
.CreateNewBlock(coinbaseScript->reserveScript));
if (!pblocktemplate.get()) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
CBlock *pblock = &pblocktemplate->block;
{
LOCK(cs_main);
IncrementExtraNonce(config, pblock, chainActive.Tip(), nExtraNonce);
}
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount &&
!CheckProofOfWork(pblock->GetHash(), pblock->nBits,
Params().GetConsensus())) {
++pblock->nNonce;
--nMaxTries;
}
if (nMaxTries == 0) {
break;
}
if (pblock->nNonce == nInnerLoopCount) {
continue;
}
std::shared_ptr<const CBlock> shared_pblock =
std::make_shared<const CBlock>(*pblock);
if (!ProcessNewBlock(config, shared_pblock, true, nullptr)) {
throw JSONRPCError(RPC_INTERNAL_ERROR,
"ProcessNewBlock, block not accepted");
}
++nHeight;
blockHashes.push_back(pblock->GetHash().GetHex());
// Mark script as important because it was used at least for one
// coinbase output if the script came from the wallet.
if (keepScript) {
coinbaseScript->KeepScript();
}
}
return blockHashes;
}
static UniValue generate(const Config &config, const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"generate nblocks ( maxtries )\n"
"\nMine up to nblocks blocks immediately (before the RPC call "
"returns)\n"
"\nArguments:\n"
"1. nblocks (numeric, required) How many blocks are generated "
"immediately.\n"
"2. maxtries (numeric, optional) How many iterations to try "
"(default = 1000000).\n"
"\nResult:\n"
"[ blockhashes ] (array) hashes of blocks generated\n"
"\nExamples:\n"
"\nGenerate 11 blocks\n" +
HelpExampleCli("generate", "11"));
}
int nGenerate = request.params[0].get_int();
uint64_t nMaxTries = 1000000;
if (request.params.size() > 1) {
nMaxTries = request.params[1].get_int();
}
std::shared_ptr<CReserveScript> coinbaseScript;
GetMainSignals().ScriptForMining(coinbaseScript);
// If the keypool is exhausted, no script is returned at all. Catch this.
if (!coinbaseScript) {
throw JSONRPCError(
RPC_WALLET_KEYPOOL_RAN_OUT,
"Error: Keypool ran out, please call keypoolrefill first");
}
// Throw an error if no script was provided.
if (coinbaseScript->reserveScript.empty()) {
throw JSONRPCError(
RPC_INTERNAL_ERROR,
"No coinbase script available (mining requires a wallet)");
}
return generateBlocks(config, coinbaseScript, nGenerate, nMaxTries, true);
}
static UniValue generatetoaddress(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 2 ||
request.params.size() > 3) {
throw std::runtime_error(
"generatetoaddress nblocks address (maxtries)\n"
"\nMine blocks immediately to a specified address (before the RPC "
"call returns)\n"
"\nArguments:\n"
"1. nblocks (numeric, required) How many blocks are generated "
"immediately.\n"
"2. address (string, required) The address to send the newly "
"generated bitcoin to.\n"
"3. maxtries (numeric, optional) How many iterations to try "
"(default = 1000000).\n"
"\nResult:\n"
"[ blockhashes ] (array) hashes of blocks generated\n"
"\nExamples:\n"
"\nGenerate 11 blocks to myaddress\n" +
HelpExampleCli("generatetoaddress", "11 \"myaddress\""));
}
int nGenerate = request.params[0].get_int();
uint64_t nMaxTries = 1000000;
if (request.params.size() > 2) {
nMaxTries = request.params[2].get_int();
}
CTxDestination destination = DecodeDestination(request.params[1].get_str());
if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Error: Invalid address");
}
std::shared_ptr<CReserveScript> coinbaseScript(new CReserveScript());
coinbaseScript->reserveScript = GetScriptForDestination(destination);
return generateBlocks(config, coinbaseScript, nGenerate, nMaxTries, false);
}
static UniValue getmininginfo(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 0) {
throw std::runtime_error(
"getmininginfo\n"
"\nReturns a json object containing mining-related information."
"\nResult:\n"
"{\n"
" \"blocks\": nnn, (numeric) The current block\n"
" \"currentblocksize\": nnn, (numeric) The last block size\n"
" \"currentblocktx\": nnn, (numeric) The last block "
"transaction\n"
" \"difficulty\": xxx.xxxxx (numeric) The current difficulty\n"
" \"errors\": \"...\" (string) Current errors\n"
" \"networkhashps\": nnn, (numeric) The network hashes per "
"second\n"
" \"pooledtx\": n (numeric) The size of the mempool\n"
" \"chain\": \"xxxx\", (string) current network name as "
"defined in BIP70 (main, test, regtest)\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getmininginfo", "") +
HelpExampleRpc("getmininginfo", ""));
}
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("blocks", int(chainActive.Height())));
obj.push_back(Pair("currentblocksize", uint64_t(nLastBlockSize)));
obj.push_back(Pair("currentblocktx", uint64_t(nLastBlockTx)));
obj.push_back(Pair("difficulty", double(GetDifficulty(chainActive.Tip()))));
obj.push_back(Pair("blockprioritypercentage",
uint8_t(GetArg("-blockprioritypercentage",
DEFAULT_BLOCK_PRIORITY_PERCENTAGE))));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
obj.push_back(Pair("networkhashps", getnetworkhashps(config, request)));
obj.push_back(Pair("pooledtx", uint64_t(mempool.size())));
obj.push_back(Pair("chain", Params().NetworkIDString()));
return obj;
}
// NOTE: Unlike wallet RPC (which use BCH values), mining RPCs follow GBT (BIP
// 22) in using satoshi amounts
static UniValue prioritisetransaction(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 3) {
throw std::runtime_error(
"prioritisetransaction <txid> <priority delta> <fee delta>\n"
"Accepts the transaction into mined blocks at a higher (or lower) "
"priority\n"
"\nArguments:\n"
"1. \"txid\" (string, required) The transaction id.\n"
"2. priority_delta (numeric, required) The priority to add or "
"subtract.\n"
" The transaction selection algorithm considers "
"the tx as it would have a higher priority.\n"
" (priority of a transaction is calculated: "
"coinage * value_in_satoshis / txsize) \n"
"3. fee_delta (numeric, required) The fee value (in satoshis) "
"to add (or subtract, if negative).\n"
" The fee is not actually paid, only the "
"algorithm for selecting transactions into a block\n"
" considers the transaction as it would have paid "
"a higher (or lower) fee.\n"
"\nResult:\n"
"true (boolean) Returns true\n"
"\nExamples:\n" +
HelpExampleCli("prioritisetransaction", "\"txid\" 0.0 10000") +
HelpExampleRpc("prioritisetransaction", "\"txid\", 0.0, 10000"));
}
LOCK(cs_main);
uint256 hash = ParseHashStr(request.params[0].get_str(), "txid");
Amount nAmount(request.params[2].get_int64());
mempool.PrioritiseTransaction(hash, request.params[0].get_str(),
request.params[1].get_real(), nAmount);
return true;
}
// NOTE: Assumes a conclusive result; if result is inconclusive, it must be
// handled by caller
static UniValue BIP22ValidationResult(const Config &config,
const CValidationState &state) {
if (state.IsValid()) {
return NullUniValue;
}
std::string strRejectReason = state.GetRejectReason();
if (state.IsError()) {
throw JSONRPCError(RPC_VERIFY_ERROR, strRejectReason);
}
if (state.IsInvalid()) {
if (strRejectReason.empty()) {
return "rejected";
}
return strRejectReason;
}
// Should be impossible.
return "valid?";
}
std::string gbt_vb_name(const Consensus::DeploymentPos pos) {
const struct BIP9DeploymentInfo &vbinfo = VersionBitsDeploymentInfo[pos];
std::string s = vbinfo.name;
if (!vbinfo.gbt_force) {
s.insert(s.begin(), '!');
}
return s;
}
static UniValue getblocktemplate(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 1) {
throw std::runtime_error(
"getblocktemplate ( TemplateRequest )\n"
"\nIf the request parameters include a 'mode' key, that is used to "
"explicitly select between the default 'template' request or a "
"'proposal'.\n"
"It returns data needed to construct a block to work on.\n"
"For full specification, see BIPs 22, 23, 9, and 145:\n"
" "
"https://github.com/bitcoin/bips/blob/master/bip-0022.mediawiki\n"
" "
"https://github.com/bitcoin/bips/blob/master/bip-0023.mediawiki\n"
" "
"https://github.com/bitcoin/bips/blob/master/"
"bip-0009.mediawiki#getblocktemplate_changes\n"
" "
"https://github.com/bitcoin/bips/blob/master/bip-0145.mediawiki\n"
"\nArguments:\n"
"1. template_request (json object, optional) A json object "
"in the following spec\n"
" {\n"
" \"mode\":\"template\" (string, optional) This must be "
"set to \"template\", \"proposal\" (see BIP 23), or omitted\n"
" \"capabilities\":[ (array, optional) A list of "
"strings\n"
" \"support\" (string) client side supported "
"feature, 'longpoll', 'coinbasetxn', 'coinbasevalue', 'proposal', "
"'serverlist', 'workid'\n"
" ,...\n"
" ],\n"
" \"rules\":[ (array, optional) A list of "
"strings\n"
" \"support\" (string) client side supported "
"softfork deployment\n"
" ,...\n"
" ]\n"
" }\n"
"\n"
"\nResult:\n"
"{\n"
" \"version\" : n, (numeric) The preferred "
"block version\n"
" \"rules\" : [ \"rulename\", ... ], (array of strings) "
"specific block rules that are to be enforced\n"
" \"vbavailable\" : { (json object) set of "
"pending, supported versionbit (BIP 9) softfork deployments\n"
" \"rulename\" : bitnumber (numeric) identifies the "
"bit number as indicating acceptance and readiness for the named "
"softfork rule\n"
" ,...\n"
" },\n"
" \"vbrequired\" : n, (numeric) bit mask of "
"versionbits the server requires set in submissions\n"
" \"previousblockhash\" : \"xxxx\", (string) The hash of "
"current highest block\n"
" \"transactions\" : [ (array) contents of "
"non-coinbase transactions that should be included in the next "
"block\n"
" {\n"
" \"data\" : \"xxxx\", (string) transaction "
"data encoded in hexadecimal (byte-for-byte)\n"
" \"txid\" : \"xxxx\", (string) transaction id "
"encoded in little-endian hexadecimal\n"
" \"hash\" : \"xxxx\", (string) hash encoded "
"in little-endian hexadecimal (including witness data)\n"
" \"depends\" : [ (array) array of numbers "
"\n"
" n (numeric) transactions "
"before this one (by 1-based index in 'transactions' list) that "
"must be present in the final block if this one is\n"
" ,...\n"
" ],\n"
" \"fee\": n, (numeric) difference in "
"value between transaction inputs and outputs (in Satoshis); for "
"coinbase transactions, this is a negative Number of the total "
"collected block fees (ie, not including the block subsidy); if "
"key is not present, fee is unknown and clients MUST NOT assume "
"there isn't one\n"
" \"sigops\" : n, (numeric) total SigOps "
"cost, as counted for purposes of block limits; if key is not "
"present, sigop cost is unknown and clients MUST NOT assume it is "
"zero\n"
" \"required\" : true|false (boolean) if provided and "
"true, this transaction must be in the final block\n"
" }\n"
" ,...\n"
" ],\n"
" \"coinbaseaux\" : { (json object) data that "
"should be included in the coinbase's scriptSig content\n"
" \"flags\" : \"xx\" (string) key name is to "
"be ignored, and value included in scriptSig\n"
" },\n"
" \"coinbasevalue\" : n, (numeric) maximum allowable "
"input to coinbase transaction, including the generation award and "
"transaction fees (in Satoshis)\n"
" \"coinbasetxn\" : { ... }, (json object) information "
"for coinbase transaction\n"
" \"target\" : \"xxxx\", (string) The hash target\n"
" \"mintime\" : xxx, (numeric) The minimum "
"timestamp appropriate for next block time in seconds since epoch "
"(Jan 1 1970 GMT)\n"
" \"mutable\" : [ (array of string) list of "
"ways the block template may be changed \n"
" \"value\" (string) A way the block "
"template may be changed, e.g. 'time', 'transactions', "
"'prevblock'\n"
" ,...\n"
" ],\n"
" \"noncerange\" : \"00000000ffffffff\",(string) A range of valid "
"nonces\n"
" \"sigoplimit\" : n, (numeric) limit of sigops "
"in blocks\n"
" \"sizelimit\" : n, (numeric) limit of block "
"size\n"
" \"curtime\" : ttt, (numeric) current timestamp "
"in seconds since epoch (Jan 1 1970 GMT)\n"
" \"bits\" : \"xxxxxxxx\", (string) compressed "
"target of next block\n"
" \"height\" : n (numeric) The height of the "
"next block\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getblocktemplate", "") +
HelpExampleRpc("getblocktemplate", ""));
}
LOCK(cs_main);
std::string strMode = "template";
UniValue lpval = NullUniValue;
std::set<std::string> setClientRules;
int64_t nMaxVersionPreVB = -1;
if (request.params.size() > 0) {
const UniValue &oparam = request.params[0].get_obj();
const UniValue &modeval = find_value(oparam, "mode");
if (modeval.isStr()) {
strMode = modeval.get_str();
} else if (modeval.isNull()) {
/* Do nothing */
} else {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
}
lpval = find_value(oparam, "longpollid");
if (strMode == "proposal") {
const UniValue &dataval = find_value(oparam, "data");
if (!dataval.isStr()) {
throw JSONRPCError(RPC_TYPE_ERROR,
"Missing data String key for proposal");
}
CBlock block;
if (!DecodeHexBlk(block, dataval.get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"Block decode failed");
}
uint256 hash = block.GetHash();
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = mi->second;
if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
return "duplicate";
}
if (pindex->nStatus & BLOCK_FAILED_MASK) {
return "duplicate-invalid";
}
return "duplicate-inconclusive";
}
CBlockIndex *const pindexPrev = chainActive.Tip();
// TestBlockValidity only supports blocks built on the current Tip
if (block.hashPrevBlock != pindexPrev->GetBlockHash()) {
return "inconclusive-not-best-prevblk";
}
CValidationState state;
TestBlockValidity(config, state, block, pindexPrev, false, true);
return BIP22ValidationResult(config, state);
}
const UniValue &aClientRules = find_value(oparam, "rules");
if (aClientRules.isArray()) {
for (size_t i = 0; i < aClientRules.size(); ++i) {
const UniValue &v = aClientRules[i];
setClientRules.insert(v.get_str());
}
} else {
// NOTE: It is important that this NOT be read if versionbits is
// supported
const UniValue &uvMaxVersion = find_value(oparam, "maxversion");
if (uvMaxVersion.isNum()) {
nMaxVersionPreVB = uvMaxVersion.get_int64();
}
}
}
if (strMode != "template") {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid mode");
}
if (!g_connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
if (g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL) == 0) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED,
"Bitcoin is not connected!");
}
if (IsInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
"Bitcoin is downloading blocks...");
}
static unsigned int nTransactionsUpdatedLast;
if (!lpval.isNull()) {
// Wait to respond until either the best block changes, OR a minute has
// passed and there are more transactions
uint256 hashWatchedChain;
boost::system_time checktxtime;
unsigned int nTransactionsUpdatedLastLP;
if (lpval.isStr()) {
// Format: <hashBestChain><nTransactionsUpdatedLast>
std::string lpstr = lpval.get_str();
hashWatchedChain.SetHex(lpstr.substr(0, 64));
nTransactionsUpdatedLastLP = atoi64(lpstr.substr(64));
} else {
// NOTE: Spec does not specify behaviour for non-string longpollid,
// but this makes testing easier
hashWatchedChain = chainActive.Tip()->GetBlockHash();
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
}
// Release the wallet and main lock while waiting
LEAVE_CRITICAL_SECTION(cs_main);
{
checktxtime =
boost::get_system_time() + boost::posix_time::minutes(1);
boost::unique_lock<boost::mutex> lock(csBestBlock);
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain &&
IsRPCRunning()) {
if (!cvBlockChange.timed_wait(lock, checktxtime)) {
// Timeout: Check transactions for update
if (mempool.GetTransactionsUpdated() !=
nTransactionsUpdatedLastLP) {
break;
}
checktxtime += boost::posix_time::seconds(10);
}
}
}
ENTER_CRITICAL_SECTION(cs_main);
if (!IsRPCRunning()) {
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
}
// TODO: Maybe recheck connections/IBD and (if something wrong) send an
// expires-immediately template to stop miners?
}
// Update block
static CBlockIndex *pindexPrev;
static int64_t nStart;
static std::unique_ptr<CBlockTemplate> pblocktemplate;
if (pindexPrev != chainActive.Tip() ||
(mempool.GetTransactionsUpdated() != nTransactionsUpdatedLast &&
GetTime() - nStart > 5)) {
// Clear pindexPrev so future calls make a new block, despite any
// failures from here on
pindexPrev = nullptr;
// Store the pindexBest used before CreateNewBlock, to avoid races
nTransactionsUpdatedLast = mempool.GetTransactionsUpdated();
CBlockIndex *pindexPrevNew = chainActive.Tip();
nStart = GetTime();
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
pblocktemplate =
BlockAssembler(config, Params()).CreateNewBlock(scriptDummy);
if (!pblocktemplate) {
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
}
// Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew;
}
CBlock *pblock = &pblocktemplate->block; // pointer for convenience
const Consensus::Params &consensusParams = Params().GetConsensus();
// Update nTime
- UpdateTime(pblock, consensusParams, pindexPrev);
+ UpdateTime(pblock, config, pindexPrev);
pblock->nNonce = 0;
UniValue aCaps(UniValue::VARR);
aCaps.push_back("proposal");
UniValue transactions(UniValue::VARR);
std::map<uint256, int64_t> setTxIndex;
int i = 0;
for (const auto &it : pblock->vtx) {
const CTransaction &tx = *it;
uint256 txId = tx.GetId();
setTxIndex[txId] = i++;
if (tx.IsCoinBase()) {
continue;
}
UniValue entry(UniValue::VOBJ);
entry.push_back(Pair("data", EncodeHexTx(tx)));
entry.push_back(Pair("txid", txId.GetHex()));
entry.push_back(Pair("hash", tx.GetHash().GetHex()));
UniValue deps(UniValue::VARR);
for (const CTxIn &in : tx.vin) {
if (setTxIndex.count(in.prevout.hash))
deps.push_back(setTxIndex[in.prevout.hash]);
}
entry.push_back(Pair("depends", deps));
int index_in_template = i - 1;
entry.push_back(Pair(
"fee", pblocktemplate->vTxFees[index_in_template].GetSatoshis()));
int64_t nTxSigOps = pblocktemplate->vTxSigOpsCount[index_in_template];
entry.push_back(Pair("sigops", nTxSigOps));
transactions.push_back(entry);
}
UniValue aux(UniValue::VOBJ);
aux.push_back(
Pair("flags", HexStr(COINBASE_FLAGS.begin(), COINBASE_FLAGS.end())));
arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits);
UniValue aMutable(UniValue::VARR);
aMutable.push_back("time");
aMutable.push_back("transactions");
aMutable.push_back("prevblock");
UniValue result(UniValue::VOBJ);
result.push_back(Pair("capabilities", aCaps));
UniValue aRules(UniValue::VARR);
UniValue vbavailable(UniValue::VOBJ);
for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
ThresholdState state = VersionBitsState(pindexPrev, consensusParams,
pos, versionbitscache);
switch (state) {
case THRESHOLD_DEFINED:
case THRESHOLD_FAILED:
// Not exposed to GBT at all
break;
case THRESHOLD_LOCKED_IN:
// Ensure bit is set in block version
pblock->nVersion |= VersionBitsMask(consensusParams, pos);
// FALLTHROUGH to get vbavailable set...
case THRESHOLD_STARTED: {
const struct BIP9DeploymentInfo &vbinfo =
VersionBitsDeploymentInfo[pos];
vbavailable.push_back(Pair(
gbt_vb_name(pos), consensusParams.vDeployments[pos].bit));
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
if (!vbinfo.gbt_force) {
// If the client doesn't support this, don't indicate it
// in the [default] version
pblock->nVersion &=
~VersionBitsMask(consensusParams, pos);
}
}
break;
}
case THRESHOLD_ACTIVE: {
// Add to rules only
const struct BIP9DeploymentInfo &vbinfo =
VersionBitsDeploymentInfo[pos];
aRules.push_back(gbt_vb_name(pos));
if (setClientRules.find(vbinfo.name) == setClientRules.end()) {
// Not supported by the client; make sure it's safe to
// proceed
if (!vbinfo.gbt_force) {
// If we do anything other than throw an exception here,
// be sure version/force isn't sent to old clients
throw JSONRPCError(
RPC_INVALID_PARAMETER,
strprintf("Support for '%s' rule requires explicit "
"client support",
vbinfo.name));
}
}
break;
}
}
}
result.push_back(Pair("version", pblock->nVersion));
result.push_back(Pair("rules", aRules));
result.push_back(Pair("vbavailable", vbavailable));
result.push_back(Pair("vbrequired", int(0)));
if (nMaxVersionPreVB >= 2) {
// If VB is supported by the client, nMaxVersionPreVB is -1, so we won't
// get here. Because BIP 34 changed how the generation transaction is
// serialized, we can only use version/force back to v2 blocks. This is
// safe to do [otherwise-]unconditionally only because we are throwing
// an exception above if a non-force deployment gets activated. Note
// that this can probably also be removed entirely after the first BIP9
// non-force deployment (ie, probably segwit) gets activated.
aMutable.push_back("version/force");
}
result.push_back(Pair("previousblockhash", pblock->hashPrevBlock.GetHex()));
result.push_back(Pair("transactions", transactions));
result.push_back(Pair("coinbaseaux", aux));
result.push_back(
Pair("coinbasevalue",
(int64_t)pblock->vtx[0]->vout[0].nValue.GetSatoshis()));
result.push_back(
Pair("longpollid", chainActive.Tip()->GetBlockHash().GetHex() +
i64tostr(nTransactionsUpdatedLast)));
result.push_back(Pair("target", hashTarget.GetHex()));
result.push_back(
Pair("mintime", (int64_t)pindexPrev->GetMedianTimePast() + 1));
result.push_back(Pair("mutable", aMutable));
result.push_back(Pair("noncerange", "00000000ffffffff"));
// FIXME: Allow for mining block greater than 1M.
result.push_back(
Pair("sigoplimit", GetMaxBlockSigOpsCount(DEFAULT_MAX_BLOCK_SIZE)));
result.push_back(Pair("sizelimit", DEFAULT_MAX_BLOCK_SIZE));
result.push_back(Pair("curtime", pblock->GetBlockTime()));
result.push_back(Pair("bits", strprintf("%08x", pblock->nBits)));
result.push_back(Pair("height", (int64_t)(pindexPrev->nHeight + 1)));
return result;
}
class submitblock_StateCatcher : public CValidationInterface {
public:
uint256 hash;
bool found;
CValidationState state;
submitblock_StateCatcher(const uint256 &hashIn)
: hash(hashIn), found(false), state() {}
protected:
virtual void BlockChecked(const CBlock &block,
const CValidationState &stateIn) {
if (block.GetHash() != hash) {
return;
}
found = true;
state = stateIn;
}
};
static UniValue submitblock(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"submitblock \"hexdata\" ( \"jsonparametersobject\" )\n"
"\nAttempts to submit new block to network.\n"
"The 'jsonparametersobject' parameter is currently ignored.\n"
"See https://en.bitcoin.it/wiki/BIP_0022 for full specification.\n"
"\nArguments\n"
"1. \"hexdata\" (string, required) the hex-encoded block "
"data to submit\n"
"2. \"parameters\" (string, optional) object of optional "
"parameters\n"
" {\n"
" \"workid\" : \"id\" (string, optional) if the server "
"provided a workid, it MUST be included with submissions\n"
" }\n"
"\nResult:\n"
"\nExamples:\n" +
HelpExampleCli("submitblock", "\"mydata\"") +
HelpExampleRpc("submitblock", "\"mydata\""));
}
std::shared_ptr<CBlock> blockptr = std::make_shared<CBlock>();
CBlock &block = *blockptr;
if (!DecodeHexBlk(block, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed");
}
if (block.vtx.empty() || !block.vtx[0]->IsCoinBase()) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"Block does not start with a coinbase");
}
uint256 hash = block.GetHash();
bool fBlockPresent = false;
{
LOCK(cs_main);
BlockMap::iterator mi = mapBlockIndex.find(hash);
if (mi != mapBlockIndex.end()) {
CBlockIndex *pindex = mi->second;
if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) {
return "duplicate";
}
if (pindex->nStatus & BLOCK_FAILED_MASK) {
return "duplicate-invalid";
}
// Otherwise, we might only have the header - process the block
// before returning
fBlockPresent = true;
}
}
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
bool fAccepted = ProcessNewBlock(config, blockptr, true, nullptr);
UnregisterValidationInterface(&sc);
if (fBlockPresent) {
if (fAccepted && !sc.found) {
return "duplicate-inconclusive";
}
return "duplicate";
}
if (!sc.found) {
return "inconclusive";
}
return BIP22ValidationResult(config, sc.state);
}
static UniValue estimatefee(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"estimatefee nblocks\n"
"\nEstimates the approximate fee per kilobyte needed for a "
"transaction to begin\n"
"confirmation within nblocks blocks.\n"
"\nArguments:\n"
"1. nblocks (numeric, required)\n"
"\nResult:\n"
"n (numeric) estimated fee-per-kilobyte\n"
"\n"
"A negative value is returned if not enough transactions and "
"blocks\n"
"have been observed to make an estimate.\n"
"-1 is always returned for nblocks == 1 as it is impossible to "
"calculate\n"
"a fee that is high enough to get reliably included in the next "
"block.\n"
"\nExample:\n" +
HelpExampleCli("estimatefee", "6"));
}
RPCTypeCheck(request.params, {UniValue::VNUM});
int nBlocks = request.params[0].get_int();
if (nBlocks < 1) {
nBlocks = 1;
}
CFeeRate feeRate = mempool.estimateFee(nBlocks);
if (feeRate == CFeeRate(Amount(0))) {
return -1.0;
}
return ValueFromAmount(feeRate.GetFeePerK());
}
static UniValue estimatepriority(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"estimatepriority nblocks\n"
"\nDEPRECATED. Estimates the approximate priority "
"a zero-fee transaction needs to begin\n"
"confirmation within nblocks blocks.\n"
"\nArguments:\n"
"1. nblocks (numeric, required)\n"
"\nResult:\n"
"n (numeric) estimated priority\n"
"\n"
"A negative value is returned if not enough "
"transactions and blocks\n"
"have been observed to make an estimate.\n"
"\nExample:\n" +
HelpExampleCli("estimatepriority", "6"));
}
RPCTypeCheck(request.params, {UniValue::VNUM});
int nBlocks = request.params[0].get_int();
if (nBlocks < 1) {
nBlocks = 1;
}
return mempool.estimatePriority(nBlocks);
}
static UniValue estimatesmartfee(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"estimatesmartfee nblocks\n"
"\nWARNING: This interface is unstable and may disappear or "
"change!\n"
"\nEstimates the approximate fee per kilobyte needed for a "
"transaction to begin\n"
"confirmation within nblocks blocks if possible and return the "
"number of blocks\n"
"for which the estimate is valid.\n"
"\nArguments:\n"
"1. nblocks (numeric)\n"
"\nResult:\n"
"{\n"
" \"feerate\" : x.x, (numeric) estimate fee-per-kilobyte (in "
"BCH)\n"
" \"blocks\" : n (numeric) block number where estimate "
"was found\n"
"}\n"
"\n"
"A negative value is returned if not enough transactions and "
"blocks\n"
"have been observed to make an estimate for any number of blocks.\n"
"However it will not return a value below the mempool reject fee.\n"
"\nExample:\n" +
HelpExampleCli("estimatesmartfee", "6"));
}
RPCTypeCheck(request.params, {UniValue::VNUM});
int nBlocks = request.params[0].get_int();
UniValue result(UniValue::VOBJ);
int answerFound;
CFeeRate feeRate = mempool.estimateSmartFee(nBlocks, &answerFound);
result.push_back(
Pair("feerate", feeRate == CFeeRate(Amount(0))
? -1.0
: ValueFromAmount(feeRate.GetFeePerK())));
result.push_back(Pair("blocks", answerFound));
return result;
}
static UniValue estimatesmartpriority(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error(
"estimatesmartpriority nblocks\n"
"\nDEPRECATED. WARNING: This interface is unstable and may "
"disappear or change!\n"
"\nEstimates the approximate priority a zero-fee transaction needs "
"to begin\n"
"confirmation within nblocks blocks if possible and return the "
"number of blocks\n"
"for which the estimate is valid.\n"
"\nArguments:\n"
"1. nblocks (numeric, required)\n"
"\nResult:\n"
"{\n"
" \"priority\" : x.x, (numeric) estimated priority\n"
" \"blocks\" : n (numeric) block number where estimate "
"was found\n"
"}\n"
"\n"
"A negative value is returned if not enough transactions and "
"blocks\n"
"have been observed to make an estimate for any number of blocks.\n"
"However if the mempool reject fee is set it will return 1e9 * "
"MAX_MONEY.\n"
"\nExample:\n" +
HelpExampleCli("estimatesmartpriority", "6"));
}
RPCTypeCheck(request.params, {UniValue::VNUM});
int nBlocks = request.params[0].get_int();
UniValue result(UniValue::VOBJ);
int answerFound;
double priority = mempool.estimateSmartPriority(nBlocks, &answerFound);
result.push_back(Pair("priority", priority));
result.push_back(Pair("blocks", answerFound));
return result;
}
// clang-format off
static const CRPCCommand commands[] = {
// category name actor (function) okSafeMode
// ---------- ------------------------ ---------------------- ----------
{"mining", "getnetworkhashps", getnetworkhashps, true, {"nblocks", "height"}},
{"mining", "getmininginfo", getmininginfo, true, {}},
{"mining", "prioritisetransaction", prioritisetransaction, true, {"txid", "priority_delta", "fee_delta"}},
{"mining", "getblocktemplate", getblocktemplate, true, {"template_request"}},
{"mining", "submitblock", submitblock, true, {"hexdata", "parameters"}},
{"generating", "generate", generate, true, {"nblocks", "maxtries"}},
{"generating", "generatetoaddress", generatetoaddress, true, {"nblocks", "address", "maxtries"}},
{"util", "estimatefee", estimatefee, true, {"nblocks"}},
{"util", "estimatepriority", estimatepriority, true, {"nblocks"}},
{"util", "estimatesmartfee", estimatesmartfee, true, {"nblocks"}},
{"util", "estimatesmartpriority", estimatesmartpriority, true, {"nblocks"}},
};
// clang-format on
void RegisterMiningRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Wed, May 21, 21:34 (1 d, 18 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5865979
Default Alt Text
(78 KB)
Attached To
rABC Bitcoin ABC
Event Timeline
Log In to Comment