Changeset View
Changeset View
Standalone View
Standalone View
src/miner.cpp
Show First 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | int64_t UpdateTime(CBlockHeader *pblock, const Consensus::Params ¶ms, | ||||
// Updating time can change work required on testnet: | // Updating time can change work required on testnet: | ||||
if (params.fPowAllowMinDifficultyBlocks) { | if (params.fPowAllowMinDifficultyBlocks) { | ||||
pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, params); | pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, params); | ||||
} | } | ||||
return nNewTime - nOldTime; | return nNewTime - nOldTime; | ||||
} | } | ||||
static uint64_t ComputeMaxGeneratedBlockSize(const Config &config, | BlockAssembler::Options::Options() | ||||
const CBlockIndex *pindexPrev) { | : nExcessiveBlockSize(DEFAULT_MAX_BLOCK_SIZE), | ||||
nMaxGeneratedBlockSize(DEFAULT_MAX_GENERATED_BLOCK_SIZE), | |||||
blockMinFeeRate(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB), | |||||
nBlockPriorityPercentage(DEFAULT_BLOCK_PRIORITY_PERCENTAGE) {} | |||||
BlockAssembler::BlockAssembler(const CChainParams ¶ms, | |||||
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)); | |||||
// Reserve a portion of the block for high priority transactions. | |||||
nBlockPriorityPercentage = options.nBlockPriorityPercentage; | |||||
} | |||||
static BlockAssembler::Options DefaultOptions(const Config &config) { | |||||
// Block resource limits | // Block resource limits | ||||
// If -blockmaxsize is not given, limit to DEFAULT_MAX_GENERATED_BLOCK_SIZE | // If -blockmaxsize is not given, limit to DEFAULT_MAX_GENERATED_BLOCK_SIZE | ||||
// If only one is given, only restrict the specified resource. | // If only one is given, only restrict the specified resource. | ||||
// If both are given, restrict both. | // If both are given, restrict both. | ||||
uint64_t nMaxGeneratedBlockSize = DEFAULT_MAX_GENERATED_BLOCK_SIZE; | BlockAssembler::Options options; | ||||
if (gArgs.IsArgSet("-blockmaxsize")) { | |||||
nMaxGeneratedBlockSize = | |||||
gArgs.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE); | |||||
} | |||||
// Limit size to between 1K and MaxBlockSize-1K for sanity: | options.nExcessiveBlockSize = config.GetMaxBlockSize(); | ||||
nMaxGeneratedBlockSize = | |||||
std::max(uint64_t(1000), std::min(config.GetMaxBlockSize() - 1000, | |||||
nMaxGeneratedBlockSize)); | |||||
return nMaxGeneratedBlockSize; | if (gArgs.IsArgSet("-blockmaxsize")) { | ||||
options.nMaxGeneratedBlockSize = | |||||
gArgs.GetArg("-blockmaxsize", DEFAULT_MAX_GENERATED_BLOCK_SIZE); | |||||
} | } | ||||
BlockAssembler::BlockAssembler(const Config &_config, const CTxMemPool &mpool) | |||||
: config(&_config), mempool(&mpool) { | |||||
if (gArgs.IsArgSet("-blockmintxfee")) { | if (gArgs.IsArgSet("-blockmintxfee")) { | ||||
Amount n = Amount::zero(); | Amount n = Amount::zero(); | ||||
ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n); | ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n); | ||||
blockMinFeeRate = CFeeRate(n); | options.blockMinFeeRate = CFeeRate(n); | ||||
} else { | |||||
blockMinFeeRate = CFeeRate(DEFAULT_BLOCK_MIN_TX_FEE_PER_KB); | |||||
} | } | ||||
LOCK(cs_main); | options.nBlockPriorityPercentage = config.GetBlockPriorityPercentage(); | ||||
nMaxGeneratedBlockSize = | |||||
ComputeMaxGeneratedBlockSize(*config, chainActive.Tip()); | return options; | ||||
} | } | ||||
BlockAssembler::BlockAssembler(const Config &config, const CTxMemPool &_mempool) | |||||
: BlockAssembler(config.GetChainParams(), _mempool, | |||||
DefaultOptions(config)) {} | |||||
void BlockAssembler::resetBlock() { | void BlockAssembler::resetBlock() { | ||||
inBlock.clear(); | inBlock.clear(); | ||||
// Reserve space for coinbase tx. | // Reserve space for coinbase tx. | ||||
nBlockSize = 1000; | nBlockSize = 1000; | ||||
nBlockSigOps = 100; | nBlockSigOps = 100; | ||||
// These counters do not include coinbase tx. | // These counters do not include coinbase tx. | ||||
nBlockTx = 0; | nBlockTx = 0; | ||||
nFees = Amount::zero(); | nFees = Amount::zero(); | ||||
lastFewTxs = 0; | lastFewTxs = 0; | ||||
} | } | ||||
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> | std::unique_ptr<CBlockTemplate> | ||||
BlockAssembler::CreateNewBlock(const CScript &scriptPubKeyIn) { | BlockAssembler::CreateNewBlock(const CScript &scriptPubKeyIn) { | ||||
int64_t nTimeStart = GetTimeMicros(); | int64_t nTimeStart = GetTimeMicros(); | ||||
resetBlock(); | resetBlock(); | ||||
pblocktemplate.reset(new CBlockTemplate()); | pblocktemplate.reset(new CBlockTemplate()); | ||||
if (!pblocktemplate.get()) { | if (!pblocktemplate.get()) { | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
// Pointer for convenience. | // Pointer for convenience. | ||||
pblock = &pblocktemplate->block; | pblock = &pblocktemplate->block; | ||||
// Add dummy coinbase tx as first transaction. It is updated at the end. | // 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, 0, -1); | ||||
LOCK2(cs_main, mempool->cs); | LOCK2(cs_main, mempool->cs); | ||||
CBlockIndex *pindexPrev = chainActive.Tip(); | CBlockIndex *pindexPrev = chainActive.Tip(); | ||||
assert(pindexPrev != nullptr); | assert(pindexPrev != nullptr); | ||||
nHeight = pindexPrev->nHeight + 1; | nHeight = pindexPrev->nHeight + 1; | ||||
const CChainParams &chainparams = config->GetChainParams(); | |||||
pblock->nVersion = | pblock->nVersion = | ||||
ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); | ComputeBlockVersion(pindexPrev, chainparams.GetConsensus()); | ||||
// -regtest only: allow overriding block.nVersion with | // -regtest only: allow overriding block.nVersion with | ||||
// -blockversion=N to test forking scenarios | // -blockversion=N to test forking scenarios | ||||
if (chainparams.MineBlocksOnDemand()) { | if (chainparams.MineBlocksOnDemand()) { | ||||
pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion); | pblock->nVersion = gArgs.GetArg("-blockversion", pblock->nVersion); | ||||
} | } | ||||
pblock->nTime = GetAdjustedTime(); | pblock->nTime = GetAdjustedTime(); | ||||
nMaxGeneratedBlockSize = ComputeMaxGeneratedBlockSize(*config, pindexPrev); | |||||
nMedianTimePast = pindexPrev->GetMedianTimePast(); | nMedianTimePast = pindexPrev->GetMedianTimePast(); | ||||
nLockTimeCutoff = | nLockTimeCutoff = | ||||
(STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) | (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST) | ||||
? nMedianTimePast | ? nMedianTimePast | ||||
: pblock->GetBlockTime(); | : pblock->GetBlockTime(); | ||||
addPriorityTxs(); | addPriorityTxs(); | ||||
int nPackagesSelected = 0; | int nPackagesSelected = 0; | ||||
▲ Show 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | BlockAssembler::CreateNewBlock(const CScript &scriptPubKeyIn) { | ||||
// Copy all the transactions into the block | // Copy all the transactions into the block | ||||
// FIXME: This should be removed as it is significant overhead. | // FIXME: This should be removed as it is significant overhead. | ||||
// See T479 | // See T479 | ||||
for (const CBlockTemplateEntry &tx : pblocktemplate->entries) { | for (const CBlockTemplateEntry &tx : pblocktemplate->entries) { | ||||
pblock->vtx.push_back(tx.tx); | pblock->vtx.push_back(tx.tx); | ||||
} | } | ||||
CValidationState state; | CValidationState state; | ||||
if (!TestBlockValidity(state, config->GetChainParams(), *pblock, pindexPrev, | if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, | ||||
BlockValidationOptions(*config) | BlockValidationOptions(nMaxGeneratedBlockSize) | ||||
.withCheckPoW(false) | .withCheckPoW(false) | ||||
.withCheckMerkleRoot(false))) { | .withCheckMerkleRoot(false))) { | ||||
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", | throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", | ||||
__func__, | __func__, | ||||
FormatStateMessage(state))); | FormatStateMessage(state))); | ||||
} | } | ||||
int64_t nTime2 = GetTimeMicros(); | int64_t nTime2 = GetTimeMicros(); | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Lines | |||||
* - Transaction finality (locktime) | * - Transaction finality (locktime) | ||||
* - Serialized size (in case -blockmaxsize is in use) | * - Serialized size (in case -blockmaxsize is in use) | ||||
*/ | */ | ||||
bool BlockAssembler::TestPackageTransactions( | bool BlockAssembler::TestPackageTransactions( | ||||
const CTxMemPool::setEntries &package) { | const CTxMemPool::setEntries &package) { | ||||
uint64_t nPotentialBlockSize = nBlockSize; | uint64_t nPotentialBlockSize = nBlockSize; | ||||
for (CTxMemPool::txiter it : package) { | for (CTxMemPool::txiter it : package) { | ||||
CValidationState state; | CValidationState state; | ||||
if (!ContextualCheckTransaction(config->GetChainParams().GetConsensus(), | if (!ContextualCheckTransaction(chainparams.GetConsensus(), it->GetTx(), | ||||
it->GetTx(), state, nHeight, | state, nHeight, nLockTimeCutoff, | ||||
nLockTimeCutoff, nMedianTimePast)) { | nMedianTimePast)) { | ||||
return false; | return false; | ||||
} | } | ||||
uint64_t nTxSize = | uint64_t nTxSize = | ||||
::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION); | ::GetSerializeSize(it->GetTx(), SER_NETWORK, PROTOCOL_VERSION); | ||||
if (nPotentialBlockSize + nTxSize >= nMaxGeneratedBlockSize) { | if (nPotentialBlockSize + nTxSize >= nMaxGeneratedBlockSize) { | ||||
return false; | return false; | ||||
} | } | ||||
Show All 34 Lines | if (nBlockSigOps + it->GetSigOpCount() >= maxBlockSigOps) { | ||||
// Otherwise attempt to find another tx with fewer sigops to put in the | // Otherwise attempt to find another tx with fewer sigops to put in the | ||||
// block. | // block. | ||||
return TestForBlockResult::TXCantFit; | return TestForBlockResult::TXCantFit; | ||||
} | } | ||||
// Must check that lock times are still valid. This can be removed once MTP | // 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. | // is always enforced as long as reorgs keep the mempool consistent. | ||||
CValidationState state; | CValidationState state; | ||||
if (!ContextualCheckTransaction(config->GetChainParams().GetConsensus(), | if (!ContextualCheckTransaction(chainparams.GetConsensus(), it->GetTx(), | ||||
it->GetTx(), state, nHeight, | state, nHeight, nLockTimeCutoff, | ||||
nLockTimeCutoff, nMedianTimePast)) { | nMedianTimePast)) { | ||||
return TestForBlockResult::TXCantFit; | return TestForBlockResult::TXCantFit; | ||||
} | } | ||||
return TestForBlockResult::TXFits; | return TestForBlockResult::TXFits; | ||||
} | } | ||||
void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) { | void BlockAssembler::AddToBlock(CTxMemPool::txiter iter) { | ||||
pblocktemplate->entries.emplace_back(iter->GetSharedTx(), iter->GetFee(), | pblocktemplate->entries.emplace_back(iter->GetSharedTx(), iter->GetFee(), | ||||
▲ Show 20 Lines • Show All 227 Lines • ▼ Show 20 Lines | while (mi != mempool->mapTx.get<ancestor_score>().end() || | ||||
// Update transactions that depend on each of these | // Update transactions that depend on each of these | ||||
nDescendantsUpdated += UpdatePackagesForAdded(ancestors, mapModifiedTx); | nDescendantsUpdated += UpdatePackagesForAdded(ancestors, mapModifiedTx); | ||||
} | } | ||||
} | } | ||||
void BlockAssembler::addPriorityTxs() { | void BlockAssembler::addPriorityTxs() { | ||||
// How much of the block should be dedicated to high-priority transactions, | // How much of the block should be dedicated to high-priority transactions, | ||||
// included regardless of the fees they pay. | // included regardless of the fees they pay. | ||||
if (config->GetBlockPriorityPercentage() == 0) { | if (nBlockPriorityPercentage == 0) { | ||||
return; | return; | ||||
} | } | ||||
uint64_t nBlockPrioritySize = | uint64_t nBlockPrioritySize = | ||||
nMaxGeneratedBlockSize * config->GetBlockPriorityPercentage() / 100; | nMaxGeneratedBlockSize * nBlockPriorityPercentage / 100; | ||||
// This vector will be sorted into a priority queue: | // This vector will be sorted into a priority queue: | ||||
std::vector<TxCoinAgePriority> vecPriority; | std::vector<TxCoinAgePriority> vecPriority; | ||||
TxCoinAgePriorityCompare pricomparer; | TxCoinAgePriorityCompare pricomparer; | ||||
std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash> | std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash> | ||||
waitPriMap; | waitPriMap; | ||||
typedef std::map<CTxMemPool::txiter, double, | typedef std::map<CTxMemPool::txiter, double, | ||||
CTxMemPool::CompareIteratorByHash>::iterator waitPriIter; | CTxMemPool::CompareIteratorByHash>::iterator waitPriIter; | ||||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | while (!vecPriority.empty()) { | ||||
vecPriority.push_back(TxCoinAgePriority(wpiter->second, child)); | vecPriority.push_back(TxCoinAgePriority(wpiter->second, child)); | ||||
std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); | std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer); | ||||
waitPriMap.erase(wpiter); | waitPriMap.erase(wpiter); | ||||
} | } | ||||
} | } | ||||
} | } | ||||
void IncrementExtraNonce(const Config &config, CBlock *pblock, | static const std::vector<uint8_t> | ||||
const CBlockIndex *pindexPrev, | 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) { | unsigned int &nExtraNonce) { | ||||
// Update nExtraNonce | // Update nExtraNonce | ||||
static uint256 hashPrevBlock; | static uint256 hashPrevBlock; | ||||
if (hashPrevBlock != pblock->hashPrevBlock) { | if (hashPrevBlock != pblock->hashPrevBlock) { | ||||
nExtraNonce = 0; | nExtraNonce = 0; | ||||
hashPrevBlock = pblock->hashPrevBlock; | hashPrevBlock = pblock->hashPrevBlock; | ||||
} | } | ||||
++nExtraNonce; | ++nExtraNonce; | ||||
// Height first in coinbase required for block.version=2 | // Height first in coinbase required for block.version=2 | ||||
unsigned int nHeight = pindexPrev->nHeight + 1; | unsigned int nHeight = pindexPrev->nHeight + 1; | ||||
CMutableTransaction txCoinbase(*pblock->vtx[0]); | CMutableTransaction txCoinbase(*pblock->vtx[0]); | ||||
txCoinbase.vin[0].scriptSig = | txCoinbase.vin[0].scriptSig = | ||||
(CScript() << nHeight << CScriptNum(nExtraNonce) | (CScript() << nHeight << CScriptNum(nExtraNonce) | ||||
<< getExcessiveBlockSizeSig(config)) + | << getExcessiveBlockSizeSig(nExcessiveBlockSize)) + | ||||
COINBASE_FLAGS; | COINBASE_FLAGS; | ||||
// Make sure the coinbase is big enough. | // Make sure the coinbase is big enough. | ||||
uint64_t coinbaseSize = | uint64_t coinbaseSize = | ||||
::GetSerializeSize(txCoinbase, SER_NETWORK, PROTOCOL_VERSION); | ::GetSerializeSize(txCoinbase, SER_NETWORK, PROTOCOL_VERSION); | ||||
if (coinbaseSize < MIN_TX_SIZE) { | if (coinbaseSize < MIN_TX_SIZE) { | ||||
txCoinbase.vin[0].scriptSig | txCoinbase.vin[0].scriptSig | ||||
<< std::vector<uint8_t>(MIN_TX_SIZE - coinbaseSize - 1); | << std::vector<uint8_t>(MIN_TX_SIZE - coinbaseSize - 1); | ||||
Show All 9 Lines |