Page MenuHomePhabricator

No OneTemporary

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

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)

Event Timeline