Skip to content

Commit 553cad9

Browse files
committed
Rewrite CreateNewBlock
Use the score index on the mempool to only add sorted txs in order. Remove much of the validation while building the block, relying on mempool to be consistent and only contain txs that can be mined. The mempool is assumed to be consistent as far as not containing txs which spend non-existent outputs or double spends, and scripts are valid. Finality of txs is still checked (except not coinbase maturity, assumed in mempool). Still TestBlockValidity in case mempool consistency breaks and return error state if an invalid block was created. Unit tests are modified to realize that invalid blocks can now be constructed if the mempool breaks its consistency assumptions and also updated to have the right fees, since the cached value is now used for block construction. Conflicts: src/miner.cpp
1 parent 5f12263 commit 553cad9

File tree

2 files changed

+156
-205
lines changed

2 files changed

+156
-205
lines changed

src/miner.cpp

Lines changed: 120 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include <boost/thread.hpp>
2929
#include <boost/tuple/tuple.hpp>
30+
#include <queue>
3031

3132
using namespace std;
3233

@@ -40,48 +41,18 @@ using namespace std;
4041
// transactions in the memory pool. When we select transactions from the
4142
// pool, we select by highest priority or fee rate, so we might consider
4243
// transactions that depend on transactions that aren't yet in the block.
43-
// The COrphan class keeps track of these 'temporary orphans' while
44-
// CreateBlock is figuring out which transactions to include.
45-
//
46-
class COrphan
47-
{
48-
public:
49-
const CTransaction* ptx;
50-
set<uint256> setDependsOn;
51-
CFeeRate feeRate;
52-
double dPriority;
53-
54-
COrphan(const CTransaction* ptxIn) : ptx(ptxIn), feeRate(0), dPriority(0)
55-
{
56-
}
57-
};
5844

5945
uint64_t nLastBlockTx = 0;
6046
uint64_t nLastBlockSize = 0;
6147

62-
// We want to sort transactions by priority and fee rate, so:
63-
typedef boost::tuple<double, CFeeRate, const CTransaction*> TxPriority;
64-
class TxPriorityCompare
48+
class ScoreCompare
6549
{
66-
bool byFee;
67-
6850
public:
69-
TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
51+
ScoreCompare() {}
7052

71-
bool operator()(const TxPriority& a, const TxPriority& b)
53+
bool operator()(const CTxMemPool::txiter a, const CTxMemPool::txiter b)
7254
{
73-
if (byFee)
74-
{
75-
if (a.get<1>() == b.get<1>())
76-
return a.get<0>() < b.get<0>();
77-
return a.get<1>() < b.get<1>();
78-
}
79-
else
80-
{
81-
if (a.get<0>() == b.get<0>())
82-
return a.get<1>() < b.get<1>();
83-
return a.get<0>() < b.get<0>();
84-
}
55+
return CompareTxMemPoolEntryByScore()(*b,*a); // Convert to less than
8556
}
8657
};
8758

@@ -141,6 +112,22 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
141112
nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
142113

143114
// Collect memory pool transactions into the block
115+
CTxMemPool::setEntries inBlock;
116+
CTxMemPool::setEntries waitSet;
117+
118+
// This vector will be sorted into a priority queue:
119+
vector<TxCoinAgePriority> vecPriority;
120+
TxCoinAgePriorityCompare pricomparer;
121+
std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash> waitPriMap;
122+
typedef std::map<CTxMemPool::txiter, double, CTxMemPool::CompareIteratorByHash>::iterator waitPriIter;
123+
double actualPriority = -1;
124+
125+
std::priority_queue<CTxMemPool::txiter, std::vector<CTxMemPool::txiter>, ScoreCompare> clearedTxs;
126+
bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
127+
uint64_t nBlockSize = 1000;
128+
uint64_t nBlockTx = 0;
129+
unsigned int nBlockSigOps = 100;
130+
int lastFewTxs = 0;
144131
CAmount nFees = 0;
145132

146133
{
@@ -149,157 +136,102 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
149136
const int nHeight = pindexPrev->nHeight + 1;
150137
pblock->nTime = GetAdjustedTime();
151138
const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast();
152-
CCoinsViewCache view(pcoinsTip);
153-
154-
// Priority order to process transactions
155-
list<COrphan> vOrphan; // list memory doesn't move
156-
map<uint256, vector<COrphan*> > mapDependers;
157-
bool fPrintPriority = GetBoolArg("-printpriority", DEFAULT_PRINTPRIORITY);
158-
159-
// This vector will be sorted into a priority queue:
160-
vector<TxPriority> vecPriority;
161-
vecPriority.reserve(mempool.mapTx.size());
162-
for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
163-
mi != mempool.mapTx.end(); ++mi)
164-
{
165-
const CTransaction& tx = mi->GetTx();
166-
167-
int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
168-
? nMedianTimePast
169-
: pblock->GetBlockTime();
170-
171-
if (tx.IsCoinBase() || !IsFinalTx(tx, nHeight, nLockTimeCutoff))
172-
continue;
173-
174-
COrphan* porphan = NULL;
175-
double dPriority = 0;
176-
CAmount nTotalIn = 0;
177-
bool fMissingInputs = false;
178-
BOOST_FOREACH(const CTxIn& txin, tx.vin)
179-
{
180-
// Read prev transaction
181-
if (!view.HaveCoins(txin.prevout.hash))
182-
{
183-
// This should never happen; all transactions in the memory
184-
// pool should connect to either transactions in the chain
185-
// or other transactions in the memory pool.
186-
if (!mempool.mapTx.count(txin.prevout.hash))
187-
{
188-
LogPrintf("ERROR: mempool transaction missing input\n");
189-
if (fDebug) assert("mempool transaction missing input" == 0);
190-
fMissingInputs = true;
191-
if (porphan)
192-
vOrphan.pop_back();
193-
break;
194-
}
195-
196-
// Has to wait for dependencies
197-
if (!porphan)
198-
{
199-
// Use list for automatic deletion
200-
vOrphan.push_back(COrphan(&tx));
201-
porphan = &vOrphan.back();
202-
}
203-
mapDependers[txin.prevout.hash].push_back(porphan);
204-
porphan->setDependsOn.insert(txin.prevout.hash);
205-
nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue;
206-
continue;
207-
}
208-
const CCoins* coins = view.AccessCoins(txin.prevout.hash);
209-
assert(coins);
210-
211-
CAmount nValueIn = coins->vout[txin.prevout.n].nValue;
212-
nTotalIn += nValueIn;
213-
214-
int nConf = nHeight - coins->nHeight;
215-
216-
dPriority += (double)nValueIn * nConf;
217-
}
218-
if (fMissingInputs) continue;
219-
220-
// Priority is sum(valuein * age) / modified_txsize
221-
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
222-
dPriority = tx.ComputePriority(dPriority, nTxSize);
223-
224-
uint256 hash = tx.GetHash();
225-
mempool.ApplyDeltas(hash, dPriority, nTotalIn);
226139

227-
CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize);
140+
int64_t nLockTimeCutoff = (STANDARD_LOCKTIME_VERIFY_FLAGS & LOCKTIME_MEDIAN_TIME_PAST)
141+
? nMedianTimePast
142+
: pblock->GetBlockTime();
228143

229-
if (porphan)
144+
bool fPriorityBlock = nBlockPrioritySize > 0;
145+
if (fPriorityBlock) {
146+
vecPriority.reserve(mempool.mapTx.size());
147+
for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin();
148+
mi != mempool.mapTx.end(); ++mi)
230149
{
231-
porphan->dPriority = dPriority;
232-
porphan->feeRate = feeRate;
150+
double dPriority = mi->GetPriority(nHeight);
151+
CAmount dummy;
152+
mempool.ApplyDeltas(mi->GetTx().GetHash(), dPriority, dummy);
153+
vecPriority.push_back(TxCoinAgePriority(dPriority, mi));
233154
}
234-
else
235-
vecPriority.push_back(TxPriority(dPriority, feeRate, &(mi->GetTx())));
155+
std::make_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
236156
}
237157

238-
// Collect transactions into block
239-
uint64_t nBlockSize = 1000;
240-
uint64_t nBlockTx = 0;
241-
int nBlockSigOps = 100;
242-
bool fSortedByFee = (nBlockPrioritySize <= 0);
158+
CTxMemPool::indexed_transaction_set::nth_index<3>::type::iterator mi = mempool.mapTx.get<3>().begin();
159+
CTxMemPool::txiter iter;
243160

244-
TxPriorityCompare comparer(fSortedByFee);
245-
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
246-
247-
while (!vecPriority.empty())
161+
while (mi != mempool.mapTx.get<3>().end() || !clearedTxs.empty())
248162
{
249-
// Take highest priority transaction off the priority queue:
250-
double dPriority = vecPriority.front().get<0>();
251-
CFeeRate feeRate = vecPriority.front().get<1>();
252-
const CTransaction& tx = *(vecPriority.front().get<2>());
253-
254-
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
255-
vecPriority.pop_back();
256-
257-
// Size limits
258-
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
259-
if (nBlockSize + nTxSize >= nBlockMaxSize)
260-
continue;
163+
bool priorityTx = false;
164+
if (fPriorityBlock && !vecPriority.empty()) { // add a tx from priority queue to fill the blockprioritysize
165+
priorityTx = true;
166+
iter = vecPriority.front().second;
167+
actualPriority = vecPriority.front().first;
168+
std::pop_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
169+
vecPriority.pop_back();
170+
}
171+
else if (clearedTxs.empty()) { // add tx with next highest score
172+
iter = mempool.mapTx.project<0>(mi);
173+
mi++;
174+
}
175+
else { // try to add a previously postponed child tx
176+
iter = clearedTxs.top();
177+
clearedTxs.pop();
178+
}
261179

262-
// Legacy limits on sigOps:
263-
unsigned int nTxSigOps = GetLegacySigOpCount(tx);
264-
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
265-
continue;
180+
if (inBlock.count(iter))
181+
continue; // could have been added to the priorityBlock
266182

267-
// Skip free transactions if we're past the minimum block size:
268-
const uint256& hash = tx.GetHash();
269-
double dPriorityDelta = 0;
270-
CAmount nFeeDelta = 0;
271-
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
272-
if (fSortedByFee && (dPriorityDelta <= 0) && (nFeeDelta <= 0) && (feeRate < ::minRelayTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
273-
continue;
183+
const CTransaction& tx = iter->GetTx();
274184

275-
// Prioritise by fee once past the priority size or we run out of high-priority
276-
// transactions:
277-
if (!fSortedByFee &&
278-
((nBlockSize + nTxSize >= nBlockPrioritySize) || !AllowFree(dPriority)))
185+
bool fOrphan = false;
186+
BOOST_FOREACH(CTxMemPool::txiter parent, mempool.GetMemPoolParents(iter))
279187
{
280-
fSortedByFee = true;
281-
comparer = TxPriorityCompare(fSortedByFee);
282-
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
188+
if (!inBlock.count(parent)) {
189+
fOrphan = true;
190+
break;
191+
}
283192
}
284-
285-
if (!view.HaveInputs(tx))
193+
if (fOrphan) {
194+
if (priorityTx)
195+
waitPriMap.insert(std::make_pair(iter,actualPriority));
196+
else
197+
waitSet.insert(iter);
286198
continue;
199+
}
287200

288-
CAmount nTxFees = view.GetValueIn(tx)-tx.GetValueOut();
289-
290-
nTxSigOps += GetP2SHSigOpCount(tx, view);
291-
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
201+
unsigned int nTxSize = iter->GetTxSize();
202+
if (fPriorityBlock &&
203+
(nBlockSize + nTxSize >= nBlockPrioritySize || !AllowFree(actualPriority))) {
204+
fPriorityBlock = false;
205+
waitPriMap.clear();
206+
}
207+
if (!priorityTx &&
208+
(iter->GetModifiedFee() < ::minRelayTxFee.GetFee(nTxSize) && nBlockSize >= nBlockMinSize)) {
209+
break;
210+
}
211+
if (nBlockSize + nTxSize >= nBlockMaxSize) {
212+
if (nBlockSize > nBlockMaxSize - 100 || lastFewTxs > 50) {
213+
break;
214+
}
215+
// Once we're within 1000 bytes of a full block, only look at 50 more txs
216+
// to try to fill the remaining space.
217+
if (nBlockSize > nBlockMaxSize - 1000) {
218+
lastFewTxs++;
219+
}
292220
continue;
221+
}
293222

294-
// Note that flags: we don't want to set mempool/IsStandard()
295-
// policy here, but we still have to ensure that the block we
296-
// create only contains transactions that are valid in new blocks.
297-
CValidationState state;
298-
if (!CheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true))
223+
if (!IsFinalTx(tx, nHeight, nLockTimeCutoff))
299224
continue;
300225

301-
UpdateCoins(tx, state, view, nHeight);
226+
unsigned int nTxSigOps = iter->GetSigOpCount();
227+
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS) {
228+
if (nBlockSigOps > MAX_BLOCK_SIGOPS - 2) {
229+
break;
230+
}
231+
continue;
232+
}
302233

234+
CAmount nTxFees = iter->GetFee();
303235
// Added
304236
pblock->vtx.push_back(tx);
305237
pblocktemplate->vTxFees.push_back(nTxFees);
@@ -311,31 +243,37 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
311243

312244
if (fPrintPriority)
313245
{
246+
double dPriority = iter->GetPriority(nHeight);
247+
CAmount dummy;
248+
mempool.ApplyDeltas(tx.GetHash(), dPriority, dummy);
314249
LogPrintf("priority %.1f fee %s txid %s\n",
315-
dPriority, feeRate.ToString(), tx.GetHash().ToString());
250+
dPriority , CFeeRate(iter->GetModifiedFee(), nTxSize).ToString(), tx.GetHash().ToString());
316251
}
317252

253+
inBlock.insert(iter);
254+
318255
// Add transactions that depend on this one to the priority queue
319-
if (mapDependers.count(hash))
256+
BOOST_FOREACH(CTxMemPool::txiter child, mempool.GetMemPoolChildren(iter))
320257
{
321-
BOOST_FOREACH(COrphan* porphan, mapDependers[hash])
322-
{
323-
if (!porphan->setDependsOn.empty())
324-
{
325-
porphan->setDependsOn.erase(hash);
326-
if (porphan->setDependsOn.empty())
327-
{
328-
vecPriority.push_back(TxPriority(porphan->dPriority, porphan->feeRate, porphan->ptx));
329-
std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
330-
}
258+
if (fPriorityBlock) {
259+
waitPriIter wpiter = waitPriMap.find(child);
260+
if (wpiter != waitPriMap.end()) {
261+
vecPriority.push_back(TxCoinAgePriority(wpiter->second,child));
262+
std::push_heap(vecPriority.begin(), vecPriority.end(), pricomparer);
263+
waitPriMap.erase(wpiter);
264+
}
265+
}
266+
else {
267+
if (waitSet.count(child)) {
268+
clearedTxs.push(child);
269+
waitSet.erase(child);
331270
}
332271
}
333272
}
334273
}
335-
336274
nLastBlockTx = nBlockTx;
337275
nLastBlockSize = nBlockSize;
338-
LogPrintf("CreateNewBlock(): total size %u\n", nBlockSize);
276+
LogPrintf("CreateNewBlock(): total size %u txs: %u fees: %ld sigops %d\n", nBlockSize, nBlockTx, nFees, nBlockSigOps);
339277

340278
// Compute final coinbase transaction.
341279
txNew.vout[0].nValue = nFees + GetBlockSubsidy(nHeight, chainparams.GetConsensus());
@@ -351,8 +289,9 @@ CBlockTemplate* CreateNewBlock(const CChainParams& chainparams, const CScript& s
351289
pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(pblock->vtx[0]);
352290

353291
CValidationState state;
354-
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false))
355-
throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed");
292+
if (!TestBlockValidity(state, chainparams, *pblock, pindexPrev, false, false)) {
293+
throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state)));
294+
}
356295
}
357296

358297
return pblocktemplate.release();

0 commit comments

Comments
 (0)