Page MenuHomePhabricator

No OneTemporary

diff --git a/src/miner.cpp b/src/miner.cpp
index 0becdf6f24..5a8cc50e93 100644
--- a/src/miner.cpp
+++ b/src/miner.cpp
@@ -1,560 +1,555 @@
// 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/activation.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/tx_verify.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/moneystr.h>
#include <util/system.h>
#include <validation.h>
#include <validationinterface.h>
#include <algorithm>
#include <queue>
#include <utility>
// 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 fee rate of a transaction combined with all
// its ancestors.
uint64_t nLastBlockTx = 0;
uint64_t nLastBlockSize = 0;
int64_t UpdateTime(CBlockHeader *pblock, const Consensus::Params &params,
const CBlockIndex *pindexPrev) {
int64_t nOldTime = pblock->nTime;
int64_t nNewTime =
std::max(pindexPrev->GetMedianTimePast() + 1, GetAdjustedTime());
if (nOldTime < nNewTime) {
pblock->nTime = nNewTime;
}
// Updating time can change work required on testnet:
if (params.fPowAllowMinDifficultyBlocks) {
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, params);
}
return nNewTime - nOldTime;
}
BlockAssembler::Options::Options()
: nExcessiveBlockSize(DEFAULT_MAX_BLOCK_SIZE),
nMaxGeneratedBlockSize(DEFAULT_MAX_GENERATED_BLOCK_SIZE),
blockMinFeeRate(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB) {}
BlockAssembler::BlockAssembler(const CChainParams &params,
const CTxMemPool &_mempool,
const Options &options)
: chainparams(params), mempool(&_mempool) {
blockMinFeeRate = options.blockMinFeeRate;
// Limit size to between 1K and options.nExcessiveBlockSize -1K for sanity:
nMaxGeneratedBlockSize = std::max<uint64_t>(
1000, std::min<uint64_t>(options.nExcessiveBlockSize - 1000,
options.nMaxGeneratedBlockSize));
}
static BlockAssembler::Options DefaultOptions(const Config &config) {
// 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.
BlockAssembler::Options options;
options.nExcessiveBlockSize = config.GetMaxBlockSize();
if (gArgs.IsArgSet("-blockmaxsize")) {
options.nMaxGeneratedBlockSize =
gArgs.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE);
}
Amount n = Amount::zero();
if (gArgs.IsArgSet("-blockmintxfee") &&
ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) {
options.blockMinFeeRate = CFeeRate(n);
}
return options;
}
BlockAssembler::BlockAssembler(const Config &config, const CTxMemPool &_mempool)
: BlockAssembler(config.GetChainParams(), _mempool,
DefaultOptions(config)) {}
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::zero();
}
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. It is updated at the end.
- pblocktemplate->entries.emplace_back(CTransactionRef(), -SATOSHI, 0, -1);
+ pblocktemplate->entries.emplace_back(CTransactionRef(), -SATOSHI, -1);
LOCK2(cs_main, mempool->cs);
CBlockIndex *pindexPrev = chainActive.Tip();
assert(pindexPrev != nullptr);
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 = gArgs.GetArg("-blockversion", pblock->nVersion);
}
pblock->nTime = GetAdjustedTime();
nMedianTimePast = pindexPrev->GetMedianTimePast();
nLockTimeCutoff =
(STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
? nMedianTimePast
: pblock->GetBlockTime();
int nPackagesSelected = 0;
int nDescendantsUpdated = 0;
addPackageTxs(nPackagesSelected, nDescendantsUpdated);
if (IsMagneticAnomalyEnabled(chainparams.GetConsensus(), pindexPrev)) {
// If magnetic anomaly is enabled, we make sure transaction are
// canonically ordered.
// FIXME: Use a zipped list. See T479
std::sort(std::begin(pblocktemplate->entries) + 1,
std::end(pblocktemplate->entries),
[](const CBlockTemplateEntry &a, const CBlockTemplateEntry &b)
-> bool { return a.tx->GetId() < b.tx->GetId(); });
}
int64_t nTime1 = GetTimeMicros();
nLastBlockTx = nBlockTx;
nLastBlockSize = nBlockSize;
// Create coinbase transaction.
CMutableTransaction coinbaseTx;
coinbaseTx.vin.resize(1);
coinbaseTx.vin[0].prevout = COutPoint();
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;
// Make sure the coinbase is big enough.
uint64_t coinbaseSize = ::GetSerializeSize(coinbaseTx, PROTOCOL_VERSION);
if (coinbaseSize < MIN_TX_SIZE) {
coinbaseTx.vin[0].scriptSig
<< std::vector<uint8_t>(MIN_TX_SIZE - coinbaseSize - 1);
}
pblocktemplate->entries[0].tx = MakeTransactionRef(coinbaseTx);
- // Note: For the Coinbase, the template entry fields aside from the `tx` are
- // not used anywhere at the time of writing. The mining rpc throws out the
- // entire transaction in fact. The tx itself is only used during regtest
- // mode.
- pblocktemplate->entries[0].txFee = -1 * nFees;
+ pblocktemplate->entries[0].fees = -1 * nFees;
uint64_t nSerializeSize = GetSerializeSize(*pblock, 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);
pblock->nBits =
GetNextWorkRequired(pindexPrev, pblock, chainparams.GetConsensus());
pblock->nNonce = 0;
- pblocktemplate->entries[0].txSigOps = GetSigOpCountWithoutP2SH(
+ pblocktemplate->entries[0].sigOpCount = GetSigOpCountWithoutP2SH(
*pblocktemplate->entries[0].tx, STANDARD_SCRIPT_VERIFY_FLAGS);
// Copy all the transactions into the block
// FIXME: This should be removed as it is significant overhead.
// See T479
for (const CBlockTemplateEntry &tx : pblocktemplate->entries) {
pblock->vtx.push_back(tx.tx);
}
CValidationState state;
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev,
BlockValidationOptions(nMaxGeneratedBlockSize)
.withCheckPoW(false)
.withCheckMerkleRoot(false))) {
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s",
__func__,
FormatStateMessage(state)));
}
int64_t nTime2 = GetTimeMicros();
LogPrint(BCLog::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);
}
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) const {
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 (CTxMemPool::txiter it : package) {
CValidationState state;
if (!ContextualCheckTransaction(chainparams.GetConsensus(), it->GetTx(),
state, nHeight, nLockTimeCutoff,
nMedianTimePast)) {
return false;
}
uint64_t nTxSize = ::GetSerializeSize(it->GetTx(), PROTOCOL_VERSION);
if (nPotentialBlockSize + nTxSize >= nMaxGeneratedBlockSize) {
return false;
}
nPotentialBlockSize += nTxSize;
}
return true;
}
void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) {
pblocktemplate->entries.emplace_back(iter->GetSharedTx(), iter->GetFee(),
- iter->GetTxSize(),
iter->GetSigOpCount());
nBlockSize += iter->GetTxSize();
++nBlockTx;
nBlockSigOps += iter->GetSigOpCount();
nFees += iter->GetFee();
inBlock.insert(iter);
bool fPrintPriority =
gArgs.GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
if (fPrintPriority) {
LogPrintf(
"fee %s txid %s\n",
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 (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());
return mapModifiedTx.count(it) || inBlock.count(it) || failedTx.count(it);
}
void BlockAssembler::SortForBlock(
const CTxMemPool::setEntries &package,
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());
}
/**
* addPackageTx includes transactions paying a fee by ensuring that
* the partial ordering of transactions is maintained. That is to say
* children come after parents, despite having a potentially larger fee.
* @param[out] nPackagesSelected How many packages were selected
* @param[out] nDescendantsUpdated Number of descendant transactions updated
*/
void BlockAssembler::addPackageTxs(int &nPackagesSelected,
int &nDescendantsUpdated) {
// 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.
// 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() &&
CompareTxMemPoolEntryByAncestorFee()(
*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, sortedEntries);
for (auto &entry : sortedEntries) {
AddToBlock(entry);
// Erase from the modified set, if present
mapModifiedTx.erase(entry);
}
++nPackagesSelected;
// Update transactions that depend on each of these
nDescendantsUpdated += UpdatePackagesForAdded(ancestors, mapModifiedTx);
}
}
static const std::vector<uint8_t>
getExcessiveBlockSizeSig(uint64_t nExcessiveBlockSize) {
std::string cbmsg = "/EB" + getSubVersionEB(nExcessiveBlockSize) + "/";
std::vector<uint8_t> vec(cbmsg.begin(), cbmsg.end());
return vec;
}
void IncrementExtraNonce(CBlock *pblock, const CBlockIndex *pindexPrev,
uint64_t nExcessiveBlockSize,
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(nExcessiveBlockSize)) +
COINBASE_FLAGS;
// Make sure the coinbase is big enough.
uint64_t coinbaseSize = ::GetSerializeSize(txCoinbase, PROTOCOL_VERSION);
if (coinbaseSize < MIN_TX_SIZE) {
txCoinbase.vin[0].scriptSig
<< std::vector<uint8_t>(MIN_TX_SIZE - coinbaseSize - 1);
}
assert(txCoinbase.vin[0].scriptSig.size() <= MAX_COINBASE_SCRIPTSIG_SIZE);
assert(::GetSerializeSize(txCoinbase, PROTOCOL_VERSION) >= MIN_TX_SIZE);
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
}
diff --git a/src/miner.h b/src/miner.h
index 5c6107bd11..54439ccd47 100644
--- a/src/miner.h
+++ b/src/miner.h
@@ -1,235 +1,229 @@
// 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 CScript;
namespace Consensus {
struct Params;
}
static const bool DEFAULT_PRINTPRIORITY = false;
struct CBlockTemplateEntry {
CTransactionRef tx;
- //!< Total real fees paid by the transaction and cached to avoid parent
- //!< lookup
- Amount txFee;
- //!< Cached total size of the transaction to avoid reserializing transaction
- size_t txSize;
- //!< Cached total number of SigOps
- uint64_t txSigOps;
-
- CBlockTemplateEntry(CTransactionRef _tx, Amount _fees, uint64_t _size,
- int64_t _sigOps)
- : tx(_tx), txFee(_fees), txSize(_size), txSigOps(_sigOps) {}
+ Amount fees;
+ int64_t sigOpCount;
+
+ CBlockTemplateEntry(CTransactionRef _tx, Amount _fees, int64_t _sigOpCount)
+ : tx(_tx), fees(_fees), sigOpCount(_sigOpCount){};
};
struct CBlockTemplate {
CBlock block;
std::vector<CBlockTemplateEntry> entries;
};
// Container for tracking updates to ancestor feerate as we include (parent)
// transactions in a block
struct CTxMemPoolModifiedEntry {
explicit CTxMemPoolModifiedEntry(CTxMemPool::txiter entry) {
iter = entry;
nSizeWithAncestors = entry->GetSizeWithAncestors();
nModFeesWithAncestors = entry->GetModFeesWithAncestors();
nSigOpCountWithAncestors = entry->GetSigOpCountWithAncestors();
}
Amount GetModifiedFee() const { return iter->GetModifiedFee(); }
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }
Amount GetModFeesWithAncestors() const { return nModFeesWithAncestors; }
size_t GetTxSize() const { return iter->GetTxSize(); }
const CTransaction &GetTx() const { return iter->GetTx(); }
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;
}
};
// 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) const {
if (a->GetCountWithAncestors() != b->GetCountWithAncestors()) {
return a->GetCountWithAncestors() < b->GetCountWithAncestors();
}
return CTxMemPool::CompareIteratorById()(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>,
CompareTxMemPoolEntryByAncestorFee>>>
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 {
explicit 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;
int64_t nMedianTimePast;
const CChainParams &chainparams;
const CTxMemPool *mempool;
public:
struct Options {
Options();
uint64_t nExcessiveBlockSize;
uint64_t nMaxGeneratedBlockSize;
CFeeRate blockMinFeeRate;
};
BlockAssembler(const Config &config, const CTxMemPool &_mempool);
BlockAssembler(const CChainParams &params, const CTxMemPool &_mempool,
const Options &options);
/** 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 feerate including unconfirmed ancestors.
* Increments nPackagesSelected / nDescendantsUpdated with corresponding
* statistics from the package selection (for logging statistics).
*/
void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated)
EXCLUSIVE_LOCKS_REQUIRED(mempool->cs);
// 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) const;
/**
* 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)
EXCLUSIVE_LOCKS_REQUIRED(mempool->cs);
/** Sort the package in an order that is valid to appear in a block */
void SortForBlock(const CTxMemPool::setEntries &package,
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)
EXCLUSIVE_LOCKS_REQUIRED(mempool->cs);
};
/** Modify the extranonce in a block */
void IncrementExtraNonce(CBlock *pblock, const CBlockIndex *pindexPrev,
uint64_t nExcessiveBlockSize,
unsigned int &nExtraNonce);
int64_t UpdateTime(CBlockHeader *pblock, const Consensus::Params &params,
const CBlockIndex *pindexPrev);
#endif // BITCOIN_MINER_H
diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp
index 1914863236..3b95f5e664 100644
--- a/src/rpc/mining.cpp
+++ b/src/rpc/mining.cpp
@@ -1,826 +1,827 @@
// Copyright (c) 2010 Satoshi Nakamoto
// Copyright (c) 2009-2018 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 <blockvalidity.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 <key_io.h>
#include <miner.h>
#include <net.h>
#include <policy/policy.h>
#include <pow.h>
#include <rpc/blockchain.h>
#include <rpc/mining.h>
#include <rpc/server.h>
#include <shutdown.h>
#include <txmempool.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <validation.h>
#include <validationinterface.h>
#include <warnings.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[0].isNull() ? request.params[0].get_int() : 120,
!request.params[1].isNull() ? request.params[1].get_int() : -1);
}
UniValue generateBlocks(const Config &config,
std::shared_ptr<CReserveScript> coinbaseScript,
int nGenerate, uint64_t nMaxTries, bool keepScript) {
static const int nInnerLoopCount = 0x100000;
int nHeightEnd = 0;
int nHeight = 0;
{
// Don't keep cs_main locked.
LOCK(cs_main);
nHeight = chainActive.Height();
nHeightEnd = nHeight + nGenerate;
}
unsigned int nExtraNonce = 0;
UniValue blockHashes(UniValue::VARR);
while (nHeight < nHeightEnd && !ShutdownRequested()) {
std::unique_ptr<CBlockTemplate> pblocktemplate(
BlockAssembler(config, g_mempool)
.CreateNewBlock(coinbaseScript->reserveScript));
if (!pblocktemplate.get()) {
throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block");
}
CBlock *pblock = &pblocktemplate->block;
{
LOCK(cs_main);
IncrementExtraNonce(pblock, chainActive.Tip(),
config.GetMaxBlockSize(), nExtraNonce);
}
while (nMaxTries > 0 && pblock->nNonce < nInnerLoopCount &&
!CheckProofOfWork(pblock->GetHash(), pblock->nBits,
config.GetChainParams().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 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[2].isNull()) {
nMaxTries = request.params[2].get_int();
}
CTxDestination destination =
DecodeDestination(request.params[1].get_str(), config.GetChainParams());
if (!IsValidDestination(destination)) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,
"Error: Invalid address");
}
std::shared_ptr<CReserveScript> coinbaseScript =
std::make_shared<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"
" \"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"
" \"warnings\": \"...\" (string) any network and "
"blockchain warnings\n"
"}\n"
"\nExamples:\n" +
HelpExampleCli("getmininginfo", "") +
HelpExampleRpc("getmininginfo", ""));
}
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
obj.pushKV("blocks", int(chainActive.Height()));
obj.pushKV("currentblocksize", uint64_t(nLastBlockSize));
obj.pushKV("currentblocktx", uint64_t(nLastBlockTx));
obj.pushKV("difficulty", double(GetDifficulty(chainActive.Tip())));
obj.pushKV("networkhashps", getnetworkhashps(config, request));
obj.pushKV("pooledtx", uint64_t(g_mempool.size()));
obj.pushKV("chain", config.GetChainParams().NetworkIDString());
obj.pushKV("warnings", GetWarnings("statusbar"));
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> <dummy> <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. dummy (required) unused.\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);
TxId txid(ParseHashStr(request.params[0].get_str(), "txid"));
Amount nAmount = request.params[2].get_int64() * SATOSHI;
g_mempool.PrioritiseTransaction(txid, 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;
}
if (state.IsError()) {
throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state));
}
if (state.IsInvalid()) {
std::string strRejectReason = state.GetRejectReason();
if (strRejectReason.empty()) {
return "rejected";
}
return strRejectReason;
}
// Should be impossible.
return "valid?";
}
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"
" }\n"
"\n"
"\nResult:\n"
"{\n"
" \"version\" : n, (numeric) The preferred "
"block version\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;
if (!request.params[0].isNull()) {
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");
}
const BlockHash hash = block.GetHash();
const CBlockIndex *pindex = LookupBlockIndex(hash);
if (pindex) {
if (pindex->IsValid(BlockValidity::SCRIPTS)) {
return "duplicate";
}
if (pindex->nStatus.isInvalid()) {
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(state, config.GetChainParams(), block, pindexPrev,
BlockValidationOptions(config)
.withCheckPoW(false)
.withCheckMerkleRoot(true));
return BIP22ValidationResult(config, state);
}
}
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;
std::chrono::steady_clock::time_point 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 =
std::chrono::steady_clock::now() + std::chrono::minutes(1);
WAIT_LOCK(g_best_block_mutex, lock);
while (g_best_block == hashWatchedChain && IsRPCRunning()) {
if (g_best_block_cv.wait_until(lock, checktxtime) ==
std::cv_status::timeout) {
// Timeout: Check transactions for update
if (g_mempool.GetTransactionsUpdated() !=
nTransactionsUpdatedLastLP) {
break;
}
checktxtime += std::chrono::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() ||
(g_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 = g_mempool.GetTransactionsUpdated();
CBlockIndex *pindexPrevNew = chainActive.Tip();
nStart = GetTime();
// Create new block
CScript scriptDummy = CScript() << OP_TRUE;
pblocktemplate =
BlockAssembler(config, g_mempool).CreateNewBlock(scriptDummy);
if (!pblocktemplate) {
throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory");
}
// Need to update only after we know CreateNewBlock succeeded
pindexPrev = pindexPrevNew;
}
assert(pindexPrev);
// pointer for convenience
CBlock *pblock = &pblocktemplate->block;
// Update nTime
UpdateTime(pblock, config.GetChainParams().GetConsensus(), pindexPrev);
pblock->nNonce = 0;
UniValue aCaps(UniValue::VARR);
aCaps.push_back("proposal");
UniValue transactions(UniValue::VARR);
int index_in_template = 0;
for (const auto &it : pblock->vtx) {
const CTransaction &tx = *it;
uint256 txId = tx.GetId();
if (tx.IsCoinBase()) {
index_in_template++;
continue;
}
UniValue entry(UniValue::VOBJ);
entry.pushKV("data", EncodeHexTx(tx));
entry.pushKV("txid", txId.GetHex());
entry.pushKV("hash", tx.GetHash().GetHex());
- entry.pushKV("fee", pblocktemplate->entries[index_in_template].txFee /
- SATOSHI);
- int64_t nTxSigOps = pblocktemplate->entries[index_in_template].txSigOps;
+ entry.pushKV("fee",
+ pblocktemplate->entries[index_in_template].fees / SATOSHI);
+ int64_t nTxSigOps =
+ pblocktemplate->entries[index_in_template].sigOpCount;
entry.pushKV("sigops", nTxSigOps);
transactions.push_back(entry);
index_in_template++;
}
UniValue aux(UniValue::VOBJ);
aux.pushKV("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.pushKV("capabilities", aCaps);
result.pushKV("version", pblock->nVersion);
result.pushKV("previousblockhash", pblock->hashPrevBlock.GetHex());
result.pushKV("transactions", transactions);
result.pushKV("coinbaseaux", aux);
result.pushKV("coinbasevalue",
int64_t(pblock->vtx[0]->vout[0].nValue / SATOSHI));
result.pushKV("longpollid", chainActive.Tip()->GetBlockHash().GetHex() +
i64tostr(nTransactionsUpdatedLast));
result.pushKV("target", hashTarget.GetHex());
result.pushKV("mintime", int64_t(pindexPrev->GetMedianTimePast()) + 1);
result.pushKV("mutable", aMutable);
result.pushKV("noncerange", "00000000ffffffff");
// FIXME: Allow for mining block greater than 1M.
result.pushKV("sigoplimit", GetMaxBlockSigOpsCount(DEFAULT_MAX_BLOCK_SIZE));
result.pushKV("sizelimit", DEFAULT_MAX_BLOCK_SIZE);
result.pushKV("curtime", pblock->GetBlockTime());
result.pushKV("bits", strprintf("%08x", pblock->nBits));
result.pushKV("height", int64_t(pindexPrev->nHeight) + 1);
return result;
}
class submitblock_StateCatcher : public CValidationInterface {
public:
uint256 hash;
bool found;
CValidationState state;
explicit submitblock_StateCatcher(const uint256 &hashIn)
: hash(hashIn), found(false), state() {}
protected:
void BlockChecked(const CBlock &block,
const CValidationState &stateIn) override {
if (block.GetHash() != hash) {
return;
}
found = true;
state = stateIn;
}
};
static UniValue submitblock(const Config &config,
const JSONRPCRequest &request) {
// We allow 2 arguments for compliance with BIP22. Argument 2 is ignored.
if (request.fHelp || request.params.size() < 1 ||
request.params.size() > 2) {
throw std::runtime_error(
"submitblock \"hexdata\" ( \"dummy\" )\n"
"\nAttempts to submit new block to network.\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. \"dummy\" (optional) dummy value, for compatibility "
"with BIP22. This value is ignored.\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");
}
const BlockHash hash = block.GetHash();
{
LOCK(cs_main);
const CBlockIndex *pindex = LookupBlockIndex(hash);
if (pindex) {
if (pindex->IsValid(BlockValidity::SCRIPTS)) {
return "duplicate";
}
if (pindex->nStatus.isInvalid()) {
return "duplicate-invalid";
}
}
}
bool new_block;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
bool accepted =
ProcessNewBlock(config, blockptr, /* fForceProcessing */ true,
/* fNewBlock */ &new_block);
UnregisterValidationInterface(&sc);
if (!new_block && accepted) {
return "duplicate";
}
if (!sc.found) {
return "inconclusive";
}
return BIP22ValidationResult(config, sc.state);
}
static UniValue submitheader(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() != 1) {
throw std::runtime_error("submitheader \"hexdata\"\n"
"\nDecode the given hexdata as a header and "
"submit it as a candidate chain tip if valid."
"\nThrows when the header is invalid.\n"
"\nArguments\n"
"1. \"hexdata\" (string, required) the "
"hex-encoded block header data\n"
"\nResult:\n"
"None"
"\nExamples:\n" +
HelpExampleCli("submitheader", "\"aabbcc\"") +
HelpExampleRpc("submitheader", "\"aabbcc\""));
}
CBlockHeader h;
if (!DecodeHexBlockHeader(h, request.params[0].get_str())) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR,
"Block header decode failed");
}
{
LOCK(cs_main);
if (!LookupBlockIndex(h.hashPrevBlock)) {
throw JSONRPCError(RPC_VERIFY_ERROR,
"Must submit previous header (" +
h.hashPrevBlock.GetHex() + ") first");
}
}
CValidationState state;
ProcessNewBlockHeaders(config, {h}, state, /* ppindex */ nullptr,
/* first_invalid */ nullptr);
if (state.IsValid()) {
return NullUniValue;
}
if (state.IsError()) {
throw JSONRPCError(RPC_VERIFY_ERROR, FormatStateMessage(state));
}
throw JSONRPCError(RPC_VERIFY_ERROR, state.GetRejectReason());
}
static UniValue estimatefee(const Config &config,
const JSONRPCRequest &request) {
if (request.fHelp || request.params.size() > 0) {
throw std::runtime_error(
"estimatefee\n"
"\nEstimates the approximate fee per kilobyte needed for a "
"transaction\n"
"\nResult:\n"
"n (numeric) estimated fee-per-kilobyte\n"
"\nExample:\n" +
HelpExampleCli("estimatefee", ""));
}
return ValueFromAmount(g_mempool.estimateFee().GetFeePerK());
}
// clang-format off
static const ContextFreeRPCCommand commands[] = {
// category name actor (function) argNames
// ---------- ------------------------ ---------------------- ----------
{"mining", "getnetworkhashps", getnetworkhashps, {"nblocks", "height"}},
{"mining", "getmininginfo", getmininginfo, {}},
{"mining", "prioritisetransaction", prioritisetransaction, {"txid", "dummy", "fee_delta"}},
{"mining", "getblocktemplate", getblocktemplate, {"template_request"}},
{"mining", "submitblock", submitblock, {"hexdata", "dummy"}},
{"mining", "submitheader", submitheader, {"hexdata"}},
{"generating", "generatetoaddress", generatetoaddress, {"nblocks", "address", "maxtries"}},
{"util", "estimatefee", estimatefee, {"nblocks"}},
};
// clang-format on
void RegisterMiningRPCCommands(CRPCTable &t) {
for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
t.appendCommand(commands[vcidx].name, &commands[vcidx]);
}
diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp
index 8d31243d7e..c6f1411a8d 100644
--- a/src/test/miner_tests.cpp
+++ b/src/test/miner_tests.cpp
@@ -1,787 +1,786 @@
// Copyright (c) 2011-2016 The Bitcoin Core developers
// Copyright (c) 2017-2018 The Bitcoin 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 <chain.h>
#include <chainparams.h>
#include <coins.h>
#include <config.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <policy/policy.h>
#include <pubkey.h>
#include <script/standard.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/strencodings.h>
#include <util/system.h>
#include <validation.h>
#include <test/test_bitcoin.h>
#include <boost/test/unit_test.hpp>
#include <memory>
BOOST_FIXTURE_TEST_SUITE(miner_tests, TestingSetup)
// BOOST_CHECK_EXCEPTION predicates to check the specific validation error
class HasReason {
public:
HasReason(const std::string &reason) : m_reason(reason) {}
bool operator()(const std::runtime_error &e) const {
return std::string(e.what()).find(m_reason) != std::string::npos;
};
private:
const std::string m_reason;
};
static CFeeRate blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB);
static BlockAssembler AssemblerForTest(const CChainParams &params,
const CTxMemPool &mempool) {
BlockAssembler::Options options;
options.blockMinFeeRate = blockMinFeeRate;
return BlockAssembler(params, mempool, options);
}
static struct {
uint8_t extranonce;
uint32_t nonce;
} blockinfo[] = {
{4, 0xa4a3e223}, {2, 0x15c32f9e}, {1, 0x0375b547}, {1, 0x7004a8a5},
{2, 0xce440296}, {2, 0x52cfe198}, {1, 0x77a72cd0}, {2, 0xbb5d6f84},
{2, 0x83f30c2c}, {1, 0x48a73d5b}, {1, 0xef7dcd01}, {2, 0x6809c6c4},
{2, 0x0883ab3c}, {1, 0x087bbbe2}, {2, 0x2104a814}, {2, 0xdffb6daa},
{1, 0xee8a0a08}, {2, 0xba4237c1}, {1, 0xa70349dc}, {1, 0x344722bb},
{3, 0xd6294733}, {2, 0xec9f5c94}, {2, 0xca2fbc28}, {1, 0x6ba4f406},
{2, 0x015d4532}, {1, 0x6e119b7c}, {2, 0x43e8f314}, {2, 0x27962f38},
{2, 0xb571b51b}, {2, 0xb36bee23}, {2, 0xd17924a8}, {2, 0x6bc212d9},
{1, 0x630d4948}, {2, 0x9a4c4ebb}, {2, 0x554be537}, {1, 0xd63ddfc7},
{2, 0xa10acc11}, {1, 0x759a8363}, {2, 0xfb73090d}, {1, 0xe82c6a34},
{1, 0xe33e92d7}, {3, 0x658ef5cb}, {2, 0xba32ff22}, {5, 0x0227a10c},
{1, 0xa9a70155}, {5, 0xd096d809}, {1, 0x37176174}, {1, 0x830b8d0f},
{1, 0xc6e3910e}, {2, 0x823f3ca8}, {1, 0x99850849}, {1, 0x7521fb81},
{1, 0xaacaabab}, {1, 0xd645a2eb}, {5, 0x7aea1781}, {5, 0x9d6e4b78},
{1, 0x4ce90fd8}, {1, 0xabdc832d}, {6, 0x4a34f32a}, {2, 0xf2524c1c},
{2, 0x1bbeb08a}, {1, 0xad47f480}, {1, 0x9f026aeb}, {1, 0x15a95049},
{2, 0xd1cb95b2}, {2, 0xf84bbda5}, {1, 0x0fa62cd1}, {1, 0xe05f9169},
{1, 0x78d194a9}, {5, 0x3e38147b}, {5, 0x737ba0d4}, {1, 0x63378e10},
{1, 0x6d5f91cf}, {2, 0x88612eb8}, {2, 0xe9639484}, {1, 0xb7fabc9d},
{2, 0x19b01592}, {1, 0x5a90dd31}, {2, 0x5bd7e028}, {2, 0x94d00323},
{1, 0xa9b9c01a}, {1, 0x3a40de61}, {1, 0x56e7eec7}, {5, 0x859f7ef6},
{1, 0xfd8e5630}, {1, 0x2b0c9f7f}, {1, 0xba700e26}, {1, 0x7170a408},
{1, 0x70de86a8}, {1, 0x74d64cd5}, {1, 0x49e738a1}, {2, 0x6910b602},
{0, 0x643c565f}, {1, 0x54264b3f}, {2, 0x97ea6396}, {2, 0x55174459},
{2, 0x03e8779a}, {1, 0x98f34d8f}, {1, 0xc07b2b07}, {1, 0xdfe29668},
{1, 0x3141c7c1}, {1, 0xb3b595f4}, {1, 0x735abf08}, {5, 0x623bfbce},
{2, 0xd351e722}, {1, 0xf4ca48c9}, {1, 0x5b19c670}, {1, 0xa164bf0e},
{2, 0xbbbeb305}, {2, 0xfe1c810a},
};
static CBlockIndex CreateBlockIndex(int nHeight) {
CBlockIndex index;
index.nHeight = nHeight;
index.pprev = chainActive.Tip();
return index;
}
static bool TestSequenceLocks(const CTransaction &tx, int flags)
EXCLUSIVE_LOCKS_REQUIRED(cs_main) {
LOCK(::g_mempool.cs);
return CheckSequenceLocks(::g_mempool, tx, flags);
}
// Test suite for ancestor feerate transaction selection.
// Implemented as an additional function, rather than a separate test case, to
// allow reusing the blockchain created in CreateNewBlock_validity.
static void TestPackageSelection(const CChainParams &chainparams,
const CScript &scriptPubKey,
const std::vector<CTransactionRef> &txFirst)
EXCLUSIVE_LOCKS_REQUIRED(cs_main, ::g_mempool.cs) {
// Test the ancestor feerate transaction selection.
TestMemPoolEntryHelper entry;
// Test that a medium fee transaction will be selected after a higher fee
// rate package with a low fee rate parent.
CMutableTransaction tx;
tx.vin.resize(1);
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 0);
tx.vout.resize(1);
tx.vout[0].nValue = int64_t(5000000000LL - 1000) * SATOSHI;
// This tx has a low fee: 1000 satoshis.
// Save this txid for later use.
TxId parentTxId = tx.GetId();
g_mempool.addUnchecked(parentTxId, entry.Fee(1000 * SATOSHI)
.Time(GetTime())
.SpendsCoinbase(true)
.FromTx(tx));
// This tx has a medium fee: 10000 satoshis.
tx.vin[0].prevout = COutPoint(txFirst[1]->GetId(), 0);
tx.vout[0].nValue = int64_t(5000000000LL - 10000) * SATOSHI;
TxId mediumFeeTxId = tx.GetId();
g_mempool.addUnchecked(mediumFeeTxId, entry.Fee(10000 * SATOSHI)
.Time(GetTime())
.SpendsCoinbase(true)
.FromTx(tx));
// This tx has a high fee, but depends on the first transaction.
tx.vin[0].prevout = COutPoint(parentTxId, 0);
// 50k satoshi fee.
tx.vout[0].nValue = int64_t(5000000000LL - 1000 - 50000) * SATOSHI;
TxId highFeeTxId = tx.GetId();
g_mempool.addUnchecked(highFeeTxId, entry.Fee(50000 * SATOSHI)
.Time(GetTime())
.SpendsCoinbase(false)
.FromTx(tx));
std::unique_ptr<CBlockTemplate> pblocktemplate =
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[1]->GetId() == parentTxId);
BOOST_CHECK(pblocktemplate->block.vtx[2]->GetId() == highFeeTxId);
BOOST_CHECK(pblocktemplate->block.vtx[3]->GetId() == mediumFeeTxId);
// Test that a package below the block min tx fee doesn't get included
tx.vin[0].prevout = COutPoint(highFeeTxId, 0);
// 0 fee.
tx.vout[0].nValue = int64_t(5000000000LL - 1000 - 50000) * SATOSHI;
TxId freeTxId = tx.GetId();
g_mempool.addUnchecked(freeTxId, entry.Fee(Amount::zero()).FromTx(tx));
size_t freeTxSize = GetVirtualTransactionSize(CTransaction(tx));
// Calculate a fee on child transaction that will put the package just
// below the block min tx fee (assuming 1 child tx of the same size).
Amount feeToUse = blockMinFeeRate.GetFee(2 * freeTxSize) - SATOSHI;
tx.vin[0].prevout = COutPoint(freeTxId, 0);
tx.vout[0].nValue =
int64_t(5000000000LL - 1000 - 50000) * SATOSHI - feeToUse;
TxId lowFeeTxId = tx.GetId();
g_mempool.addUnchecked(lowFeeTxId, entry.Fee(feeToUse).FromTx(tx));
pblocktemplate =
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey);
// Verify that the free tx and the low fee tx didn't get selected.
for (const auto &txn : pblocktemplate->block.vtx) {
BOOST_CHECK(txn->GetId() != freeTxId);
BOOST_CHECK(txn->GetId() != lowFeeTxId);
}
// Test that packages above the min relay fee do get included, even if one
// of the transactions is below the min relay fee. Remove the low fee
// transaction and replace with a higher fee transaction
g_mempool.removeRecursive(CTransaction(tx));
// Now we should be just over the min relay fee.
tx.vout[0].nValue -= 2 * SATOSHI;
lowFeeTxId = tx.GetId();
g_mempool.addUnchecked(lowFeeTxId,
entry.Fee(feeToUse + 2 * SATOSHI).FromTx(tx));
pblocktemplate =
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[4]->GetId() == freeTxId);
BOOST_CHECK(pblocktemplate->block.vtx[5]->GetId() == lowFeeTxId);
// Test that transaction selection properly updates ancestor fee
// calculations as ancestor transactions get included in a block. Add a
// 0-fee transaction that has 2 outputs.
tx.vin[0].prevout = COutPoint(txFirst[2]->GetId(), 0);
tx.vout.resize(2);
tx.vout[0].nValue = int64_t(5000000000LL - 100000000) * SATOSHI;
// 1BCC output.
tx.vout[1].nValue = 100000000 * SATOSHI;
TxId freeTxId2 = tx.GetId();
g_mempool.addUnchecked(
freeTxId2, entry.Fee(Amount::zero()).SpendsCoinbase(true).FromTx(tx));
// This tx can't be mined by itself.
tx.vin[0].prevout = COutPoint(freeTxId2, 0);
tx.vout.resize(1);
feeToUse = blockMinFeeRate.GetFee(freeTxSize);
tx.vout[0].nValue = int64_t(5000000000LL - 100000000) * SATOSHI - feeToUse;
TxId lowFeeTxId2 = tx.GetId();
g_mempool.addUnchecked(
lowFeeTxId2, entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx));
pblocktemplate =
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey);
// Verify that this tx isn't selected.
for (const auto &txn : pblocktemplate->block.vtx) {
BOOST_CHECK(txn->GetId() != freeTxId2);
BOOST_CHECK(txn->GetId() != lowFeeTxId2);
}
// This tx will be mineable, and should cause lowFeeTxId2 to be selected as
// well.
tx.vin[0].prevout = COutPoint(freeTxId2, 1);
// 10k satoshi fee.
tx.vout[0].nValue = (100000000 - 10000) * SATOSHI;
g_mempool.addUnchecked(tx.GetId(), entry.Fee(10000 * SATOSHI).FromTx(tx));
pblocktemplate =
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate->block.vtx[8]->GetId() == lowFeeTxId2);
}
void TestCoinbaseMessageEB(uint64_t eb, std::string cbmsg) {
GlobalConfig config;
config.SetMaxBlockSize(eb);
CScript scriptPubKey =
CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
"a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112"
"de5c384df7ba0b8d578a4c702b6bf11d5f")
<< OP_CHECKSIG;
std::unique_ptr<CBlockTemplate> pblocktemplate =
BlockAssembler(config, g_mempool).CreateNewBlock(scriptPubKey);
CBlock *pblock = &pblocktemplate->block;
// IncrementExtraNonce creates a valid coinbase and merkleRoot
unsigned int extraNonce = 0;
IncrementExtraNonce(pblock, chainActive.Tip(), config.GetMaxBlockSize(),
extraNonce);
unsigned int nHeight = chainActive.Tip()->nHeight + 1;
std::vector<uint8_t> vec(cbmsg.begin(), cbmsg.end());
BOOST_CHECK(pblock->vtx[0]->vin[0].scriptSig ==
((CScript() << nHeight << CScriptNum(extraNonce) << vec) +
COINBASE_FLAGS));
}
// Coinbase scriptSig has to contains the correct EB value
// converted to MB, rounded down to the first decimal
BOOST_AUTO_TEST_CASE(CheckCoinbase_EB) {
TestCoinbaseMessageEB(1000001, "/EB1.0/");
TestCoinbaseMessageEB(2000000, "/EB2.0/");
TestCoinbaseMessageEB(8000000, "/EB8.0/");
TestCoinbaseMessageEB(8320000, "/EB8.3/");
}
// NOTE: These tests rely on CreateNewBlock doing its own self-validation!
BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) {
// Note that by default, these tests run with size accounting enabled.
GlobalConfig config;
const CChainParams &chainparams = config.GetChainParams();
CScript scriptPubKey =
CScript() << ParseHex("04678afdb0fe5548271967f1a67130b7105cd6a828e03909"
"a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112"
"de5c384df7ba0b8d578a4c702b6bf11d5f")
<< OP_CHECKSIG;
std::unique_ptr<CBlockTemplate> pblocktemplate;
CMutableTransaction tx;
CScript script;
TestMemPoolEntryHelper entry;
entry.nFee = 11 * SATOSHI;
entry.nHeight = 11;
fCheckpointsEnabled = false;
// Simple block creation, nothing special yet:
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams, g_mempool)
.CreateNewBlock(scriptPubKey));
// We can't make transactions until we have inputs.
// Therefore, load 100 blocks :)
int baseheight = 0;
std::vector<CTransactionRef> txFirst;
for (size_t i = 0; i < sizeof(blockinfo) / sizeof(*blockinfo); ++i) {
// pointer for convenience.
CBlock *pblock = &pblocktemplate->block;
{
LOCK(cs_main);
pblock->nVersion = 1;
pblock->nTime = chainActive.Tip()->GetMedianTimePast() + 1;
CMutableTransaction txCoinbase(*pblock->vtx[0]);
txCoinbase.nVersion = 1;
txCoinbase.vin[0].scriptSig = CScript();
txCoinbase.vin[0].scriptSig.push_back(blockinfo[i].extranonce);
txCoinbase.vin[0].scriptSig.push_back(chainActive.Height());
txCoinbase.vout.resize(1);
txCoinbase.vout[0].scriptPubKey = CScript();
pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase));
if (txFirst.size() == 0) {
baseheight = chainActive.Height();
}
if (txFirst.size() < 4) {
txFirst.push_back(pblock->vtx[0]);
}
pblock->hashMerkleRoot = BlockMerkleRoot(*pblock);
pblock->nNonce = blockinfo[i].nonce;
}
std::shared_ptr<const CBlock> shared_pblock =
std::make_shared<const CBlock>(*pblock);
BOOST_CHECK(ProcessNewBlock(config, shared_pblock, true, nullptr));
pblock->hashPrevBlock = pblock->GetHash();
}
LOCK(cs_main);
LOCK(::g_mempool.cs);
// Just to make sure we can still make simple blocks.
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams, g_mempool)
.CreateNewBlock(scriptPubKey));
const Amount BLOCKSUBSIDY = 50 * COIN;
const Amount LOWFEE = CENT;
const Amount HIGHFEE = COIN;
const Amount HIGHERFEE = 4 * COIN;
// block sigops > limit: 1000 CHECKMULTISIG + 1
tx.vin.resize(1);
// NOTE: OP_NOP is used to force 20 SigOps for the CHECKMULTISIG
tx.vin[0].scriptSig = CScript() << OP_0 << OP_0 << OP_0 << OP_NOP
<< OP_CHECKMULTISIG << OP_1;
tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 0);
tx.vout.resize(1);
tx.vout[0].nValue = BLOCKSUBSIDY;
for (unsigned int i = 0; i < 1001; ++i) {
tx.vout[0].nValue -= LOWFEE;
const TxId txid = tx.GetId();
// Only first tx spends coinbase.
bool spendsCoinbase = i == 0;
// If we don't set the # of sig ops in the CTxMemPoolEntry, template
// creation fails.
g_mempool.addUnchecked(txid, entry.Fee(LOWFEE)
.Time(GetTime())
.SpendsCoinbase(spendsCoinbase)
.FromTx(tx));
tx.vin[0].prevout = COutPoint(txid, 0);
}
BOOST_CHECK_EXCEPTION(
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey),
std::runtime_error, HasReason("bad-blk-sigops"));
g_mempool.clear();
tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 0);
tx.vout[0].nValue = BLOCKSUBSIDY;
for (unsigned int i = 0; i < 1001; ++i) {
tx.vout[0].nValue -= LOWFEE;
const TxId txid = tx.GetId();
// Only first tx spends coinbase.
bool spendsCoinbase = i == 0;
// If we do set the # of sig ops in the CTxMemPoolEntry, template
// creation passes.
g_mempool.addUnchecked(txid, entry.Fee(LOWFEE)
.Time(GetTime())
.SpendsCoinbase(spendsCoinbase)
.SigOpsCost(80)
.FromTx(tx));
tx.vin[0].prevout = COutPoint(txid, 0);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams, g_mempool)
.CreateNewBlock(scriptPubKey));
g_mempool.clear();
// block size > limit
tx.vin[0].scriptSig = CScript();
// 18 * (520char + DROP) + OP_1 = 9433 bytes
std::vector<uint8_t> vchData(520);
for (unsigned int i = 0; i < 18; ++i) {
tx.vin[0].scriptSig << vchData << OP_DROP;
}
tx.vin[0].scriptSig << OP_1;
tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 0);
tx.vout[0].nValue = BLOCKSUBSIDY;
for (unsigned int i = 0; i < 128; ++i) {
tx.vout[0].nValue -= LOWFEE;
const TxId txid = tx.GetId();
// Only first tx spends coinbase.
bool spendsCoinbase = i == 0;
g_mempool.addUnchecked(txid, entry.Fee(LOWFEE)
.Time(GetTime())
.SpendsCoinbase(spendsCoinbase)
.FromTx(tx));
tx.vin[0].prevout = COutPoint(txid, 0);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams, g_mempool)
.CreateNewBlock(scriptPubKey));
g_mempool.clear();
// Orphan in mempool, template creation fails.
TxId txid = tx.GetId();
g_mempool.addUnchecked(txid, entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx));
BOOST_CHECK_EXCEPTION(
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey),
std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
g_mempool.clear();
// Child with higher priority than parent.
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vin[0].prevout = COutPoint(txFirst[1]->GetId(), 0);
tx.vout[0].nValue = BLOCKSUBSIDY - HIGHFEE;
txid = tx.GetId();
g_mempool.addUnchecked(
txid,
entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout = COutPoint(txid, 0);
tx.vin.resize(2);
tx.vin[1].scriptSig = CScript() << OP_1;
tx.vin[1].prevout = COutPoint(txFirst[0]->GetId(), 0);
// First txn output + fresh coinbase - new txn fee.
tx.vout[0].nValue = tx.vout[0].nValue + BLOCKSUBSIDY - HIGHERFEE;
txid = tx.GetId();
g_mempool.addUnchecked(
txid,
entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams, g_mempool)
.CreateNewBlock(scriptPubKey));
g_mempool.clear();
// Coinbase in mempool, template creation fails.
tx.vin.resize(1);
tx.vin[0].prevout = COutPoint();
tx.vin[0].scriptSig = CScript() << OP_0 << OP_1;
tx.vout[0].nValue = Amount::zero();
txid = tx.GetId();
// Give it a fee so it'll get mined.
g_mempool.addUnchecked(
txid,
entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw bad-tx-coinbase
BOOST_CHECK_EXCEPTION(
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey),
std::runtime_error, HasReason("bad-tx-coinbase"));
g_mempool.clear();
// Double spend txn pair in mempool, template creation fails.
tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 0);
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = BLOCKSUBSIDY - HIGHFEE;
tx.vout[0].scriptPubKey = CScript() << OP_1;
txid = tx.GetId();
g_mempool.addUnchecked(
txid,
entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vout[0].scriptPubKey = CScript() << OP_2;
txid = tx.GetId();
g_mempool.addUnchecked(
txid,
entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
BOOST_CHECK_EXCEPTION(
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey),
std::runtime_error, HasReason("bad-txns-inputs-missingorspent"));
g_mempool.clear();
// Subsidy changing.
int nHeight = chainActive.Height();
// Create an actual 209999-long block chain (without valid blocks).
while (chainActive.Tip()->nHeight < 209999) {
CBlockIndex *prev = chainActive.Tip();
CBlockIndex *next = new CBlockIndex();
next->phashBlock = new BlockHash(InsecureRand256());
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
chainActive.SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams, g_mempool)
.CreateNewBlock(scriptPubKey));
// Extend to a 210000-long block chain.
while (chainActive.Tip()->nHeight < 210000) {
CBlockIndex *prev = chainActive.Tip();
CBlockIndex *next = new CBlockIndex();
next->phashBlock = new BlockHash(InsecureRand256());
pcoinsTip->SetBestBlock(next->GetBlockHash());
next->pprev = prev;
next->nHeight = prev->nHeight + 1;
next->BuildSkip();
chainActive.SetTip(next);
}
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams, g_mempool)
.CreateNewBlock(scriptPubKey));
// Invalid p2sh txn in mempool, template creation fails
tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 0);
tx.vin[0].scriptSig = CScript() << OP_1;
tx.vout[0].nValue = BLOCKSUBSIDY - LOWFEE;
script = CScript() << OP_0;
tx.vout[0].scriptPubKey = GetScriptForDestination(CScriptID(script));
txid = tx.GetId();
g_mempool.addUnchecked(
txid,
entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
tx.vin[0].prevout = COutPoint(txid, 0);
tx.vin[0].scriptSig = CScript()
<< std::vector<uint8_t>(script.begin(), script.end());
tx.vout[0].nValue -= LOWFEE;
txid = tx.GetId();
g_mempool.addUnchecked(
txid,
entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx));
// Should throw blk-bad-inputs
BOOST_CHECK_EXCEPTION(
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey),
std::runtime_error, HasReason("blk-bad-inputs"));
g_mempool.clear();
// Delete the dummy blocks again.
while (chainActive.Tip()->nHeight > nHeight) {
CBlockIndex *del = chainActive.Tip();
chainActive.SetTip(del->pprev);
pcoinsTip->SetBestBlock(del->pprev->GetBlockHash());
delete del->phashBlock;
delete del;
}
// non-final txs in mempool
SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1);
uint32_t flags = LOCKTIME_VERIFY_SEQUENCE | LOCKTIME_MEDIAN_TIME_PAST;
// height map
std::vector<int> prevheights;
// Relative height locked.
tx.nVersion = 2;
tx.vin.resize(1);
prevheights.resize(1);
// Only 1 transaction.
tx.vin[0].prevout = COutPoint(txFirst[0]->GetId(), 0);
tx.vin[0].scriptSig = CScript() << OP_1;
// txFirst[0] is the 2nd block
tx.vin[0].nSequence = chainActive.Tip()->nHeight + 1;
prevheights[0] = baseheight + 1;
tx.vout.resize(1);
tx.vout[0].nValue = BLOCKSUBSIDY - HIGHFEE;
tx.vout[0].scriptPubKey = CScript() << OP_1;
tx.nLockTime = 0;
txid = tx.GetId();
g_mempool.addUnchecked(
txid,
entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx));
const Consensus::Params &params = chainparams.GetConsensus();
{
// Locktime passes.
CValidationState state;
BOOST_CHECK(ContextualCheckTransactionForCurrentBlock(
params, CTransaction(tx), state, flags));
}
// Sequence locks fail.
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags));
// Sequence locks pass on 2nd block.
BOOST_CHECK(
SequenceLocks(CTransaction(tx), flags, &prevheights,
CreateBlockIndex(chainActive.Tip()->nHeight + 2)));
// Relative time locked.
tx.vin[0].prevout = COutPoint(txFirst[1]->GetId(), 0);
// txFirst[1] is the 3rd block.
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG |
(((chainActive.Tip()->GetMedianTimePast() + 1 -
chainActive[1]->GetMedianTimePast()) >>
CTxIn::SEQUENCE_LOCKTIME_GRANULARITY) +
1);
prevheights[0] = baseheight + 2;
txid = tx.GetId();
g_mempool.addUnchecked(txid, entry.Time(GetTime()).FromTx(tx));
{
// Locktime passes.
CValidationState state;
BOOST_CHECK(ContextualCheckTransactionForCurrentBlock(
params, CTransaction(tx), state, flags));
}
// Sequence locks fail.
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags));
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) {
// Trick the MedianTimePast.
chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime +=
512;
}
// Sequence locks pass 512 seconds later.
BOOST_CHECK(
SequenceLocks(CTransaction(tx), flags, &prevheights,
CreateBlockIndex(chainActive.Tip()->nHeight + 1)));
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) {
// Undo tricked MTP.
chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime -=
512;
}
// Absolute height locked.
tx.vin[0].prevout = COutPoint(txFirst[2]->GetId(), 0);
tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1;
prevheights[0] = baseheight + 3;
tx.nLockTime = chainActive.Tip()->nHeight + 1;
txid = tx.GetId();
g_mempool.addUnchecked(txid, entry.Time(GetTime()).FromTx(tx));
{
// Locktime fails.
CValidationState state;
BOOST_CHECK(!ContextualCheckTransactionForCurrentBlock(
params, CTransaction(tx), state, flags));
BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-nonfinal");
}
// Sequence locks pass.
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags));
{
// Locktime passes on 2nd block.
CValidationState state;
int64_t nMedianTimePast = chainActive.Tip()->GetMedianTimePast();
BOOST_CHECK(ContextualCheckTransaction(
params, CTransaction(tx), state, chainActive.Tip()->nHeight + 2,
nMedianTimePast, nMedianTimePast));
}
// Absolute time locked.
tx.vin[0].prevout = COutPoint(txFirst[3]->GetId(), 0);
tx.nLockTime = chainActive.Tip()->GetMedianTimePast();
prevheights.resize(1);
prevheights[0] = baseheight + 4;
txid = tx.GetId();
g_mempool.addUnchecked(txid, entry.Time(GetTime()).FromTx(tx));
{
// Locktime fails.
CValidationState state;
BOOST_CHECK(!ContextualCheckTransactionForCurrentBlock(
params, CTransaction(tx), state, flags));
BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-nonfinal");
}
// Sequence locks pass.
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags));
{
// Locktime passes 1 second later.
CValidationState state;
int64_t nMedianTimePast = chainActive.Tip()->GetMedianTimePast() + 1;
BOOST_CHECK(ContextualCheckTransaction(
params, CTransaction(tx), state, chainActive.Tip()->nHeight + 1,
nMedianTimePast, nMedianTimePast));
}
// mempool-dependent transactions (not added)
tx.vin[0].prevout = COutPoint(txid, 0);
prevheights[0] = chainActive.Tip()->nHeight + 1;
tx.nLockTime = 0;
tx.vin[0].nSequence = 0;
{
// Locktime passes.
CValidationState state;
BOOST_CHECK(ContextualCheckTransactionForCurrentBlock(
params, CTransaction(tx), state, flags));
}
// Sequence locks pass.
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags));
tx.vin[0].nSequence = 1;
// Sequence locks fail.
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags));
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG;
// Sequence locks pass.
BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags));
tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1;
// Sequence locks fail.
BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags));
pblocktemplate =
AssemblerForTest(chainparams, g_mempool).CreateNewBlock(scriptPubKey);
BOOST_CHECK(pblocktemplate);
// None of the of the absolute height/time locked tx should have made it
// into the template because we still check IsFinalTx in CreateNewBlock, but
// relative locked txs will if inconsistently added to g_mempool. For now
// these will still generate a valid template until BIP68 soft fork.
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 3UL);
// However if we advance height by 1 and time by 512, all of them should be
// mined.
for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) {
// Trick the MedianTimePast.
chainActive.Tip()->GetAncestor(chainActive.Tip()->nHeight - i)->nTime +=
512;
}
chainActive.Tip()->nHeight++;
SetMockTime(chainActive.Tip()->GetMedianTimePast() + 1);
BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams, g_mempool)
.CreateNewBlock(scriptPubKey));
BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5UL);
chainActive.Tip()->nHeight--;
SetMockTime(0);
g_mempool.clear();
TestPackageSelection(chainparams, scriptPubKey, txFirst);
fCheckpointsEnabled = true;
}
void CheckBlockMaxSize(const Config &config, uint64_t size, uint64_t expected) {
gArgs.ForceSetArg("-blockmaxsize", std::to_string(size));
BlockAssembler ba(config, g_mempool);
BOOST_CHECK_EQUAL(ba.GetMaxGeneratedBlockSize(), expected);
}
BOOST_AUTO_TEST_CASE(BlockAssembler_construction) {
GlobalConfig config;
// We are working on a fake chain and need to protect ourselves.
LOCK(cs_main);
// Test around historical 1MB (plus one byte because that's mandatory)
config.SetMaxBlockSize(ONE_MEGABYTE + 1);
CheckBlockMaxSize(config, 0, 1000);
CheckBlockMaxSize(config, 1000, 1000);
CheckBlockMaxSize(config, 1001, 1001);
CheckBlockMaxSize(config, 12345, 12345);
CheckBlockMaxSize(config, ONE_MEGABYTE - 1001, ONE_MEGABYTE - 1001);
CheckBlockMaxSize(config, ONE_MEGABYTE - 1000, ONE_MEGABYTE - 1000);
CheckBlockMaxSize(config, ONE_MEGABYTE - 999, ONE_MEGABYTE - 999);
CheckBlockMaxSize(config, ONE_MEGABYTE, ONE_MEGABYTE - 999);
// Test around default cap
config.SetMaxBlockSize(DEFAULT_MAX_BLOCK_SIZE);
// Now we can use the default max block size.
CheckBlockMaxSize(config, DEFAULT_MAX_BLOCK_SIZE - 1001,
DEFAULT_MAX_BLOCK_SIZE - 1001);
CheckBlockMaxSize(config, DEFAULT_MAX_BLOCK_SIZE - 1000,
DEFAULT_MAX_BLOCK_SIZE - 1000);
CheckBlockMaxSize(config, DEFAULT_MAX_BLOCK_SIZE - 999,
DEFAULT_MAX_BLOCK_SIZE - 1000);
CheckBlockMaxSize(config, DEFAULT_MAX_BLOCK_SIZE,
DEFAULT_MAX_BLOCK_SIZE - 1000);
// If the parameter is not specified, we use
// DEFAULT_MAX_GENERATED_BLOCK_SIZE
{
gArgs.ClearArg("-blockmaxsize");
BlockAssembler ba(config, g_mempool);
BOOST_CHECK_EQUAL(ba.GetMaxGeneratedBlockSize(),
DEFAULT_MAX_GENERATED_BLOCK_SIZE);
}
}
BOOST_AUTO_TEST_CASE(TestCBlockTemplateEntry) {
const CTransaction tx;
CTransactionRef txRef = MakeTransactionRef(tx);
- CBlockTemplateEntry txEntry(txRef, 1 * SATOSHI, 200, 10);
+ CBlockTemplateEntry txEntry(txRef, 1 * SATOSHI, 10);
BOOST_CHECK_MESSAGE(txEntry.tx == txRef, "Transactions did not match");
- BOOST_CHECK_EQUAL(txEntry.txFee, 1 * SATOSHI);
- BOOST_CHECK_EQUAL(txEntry.txSize, 200);
- BOOST_CHECK_EQUAL(txEntry.txSigOps, 10);
+ BOOST_CHECK_EQUAL(txEntry.fees, 1 * SATOSHI);
+ BOOST_CHECK_EQUAL(txEntry.sigOpCount, 10);
}
BOOST_AUTO_TEST_SUITE_END()

File Metadata

Mime Type
text/x-diff
Expires
Sun, Mar 2, 12:16 (21 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5187815
Default Alt Text
(96 KB)

Event Timeline