When creating new blocks, sort 'paid' area by fee-per-kb
Modify CreateNewBlock so that instead of processing all transactions in priority order, process the first 27K of transactions in priority order and then process the rest in fee-per-kilobyte order. This is the first, minimal step towards better a better fee-handling system for both miners and end-users; this patch should be easy to backport to the old versions of Bitcoin, and accomplishes the most important goal-- allow users to "buy their way in" to blocks using transaction fees.
This commit is contained in:
parent
29c8fb0d93
commit
c555400ca1
2 changed files with 114 additions and 31 deletions
|
@ -279,6 +279,11 @@ std::string HelpMessage()
|
|||
" -checkblocks=<n> " + _("How many blocks to check at startup (default: 2500, 0 = all)") + "\n" +
|
||||
" -checklevel=<n> " + _("How thorough the block verification is (0-6, default: 1)") + "\n" +
|
||||
" -loadblock=<file> " + _("Imports blocks from external blk000?.dat file") + "\n" +
|
||||
_("\nBlock creation options:\n") +
|
||||
" -blockminsize=<n> " + _("Minimum size, in bytes (default: 0)\n") +
|
||||
" -blockmaxsize=<n> " + _("Maximum size, in bytes (default: 250000)\n") +
|
||||
" -blockprioritysize=<n> " + _("Maximum bytes of high-priority/low-fee transactions (default: 27000)\n") +
|
||||
|
||||
" -? " + _("This help message") + "\n";
|
||||
|
||||
strUsage += string() +
|
||||
|
|
140
src/main.cpp
140
src/main.cpp
|
@ -3312,16 +3312,18 @@ public:
|
|||
CTransaction* ptx;
|
||||
set<uint256> setDependsOn;
|
||||
double dPriority;
|
||||
double dFeePerKb;
|
||||
|
||||
COrphan(CTransaction* ptxIn)
|
||||
{
|
||||
ptx = ptxIn;
|
||||
dPriority = 0;
|
||||
dPriority = dFeePerKb = 0;
|
||||
}
|
||||
|
||||
void print() const
|
||||
{
|
||||
printf("COrphan(hash=%s, dPriority=%.1f)\n", ptx->GetHash().ToString().substr(0,10).c_str(), dPriority);
|
||||
printf("COrphan(hash=%s, dPriority=%.1f, dFeePerKb=%.1f)\n",
|
||||
ptx->GetHash().ToString().substr(0,10).c_str(), dPriority, dFeePerKb);
|
||||
BOOST_FOREACH(uint256 hash, setDependsOn)
|
||||
printf(" setDependsOn %s\n", hash.ToString().substr(0,10).c_str());
|
||||
}
|
||||
|
@ -3331,6 +3333,30 @@ public:
|
|||
uint64 nLastBlockTx = 0;
|
||||
uint64 nLastBlockSize = 0;
|
||||
|
||||
// We want to sort transactions by priority and fee, so:
|
||||
typedef boost::tuple<double, double, CTransaction*> TxPriority;
|
||||
class TxPriorityCompare
|
||||
{
|
||||
bool byFee;
|
||||
public:
|
||||
TxPriorityCompare(bool _byFee) : byFee(_byFee) { }
|
||||
bool operator()(const TxPriority& a, const TxPriority& b)
|
||||
{
|
||||
if (byFee)
|
||||
{
|
||||
if (a.get<1>() == b.get<1>())
|
||||
return a.get<0>() < b.get<0>();
|
||||
return a.get<1>() < b.get<1>();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (a.get<0>() == b.get<0>())
|
||||
return a.get<1>() < b.get<1>();
|
||||
return a.get<0>() < b.get<0>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const char* pszDummy = "\0\0";
|
||||
CScript scriptDummy(std::vector<unsigned char>(pszDummy, pszDummy + sizeof(pszDummy)));
|
||||
|
||||
|
@ -3353,6 +3379,30 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
|||
// Add our coinbase tx as first transaction
|
||||
pblock->vtx.push_back(txNew);
|
||||
|
||||
// Largest block you're willing to create:
|
||||
unsigned int nBlockMaxSize = GetArg("-blockmaxsize", MAX_BLOCK_SIZE_GEN/2);
|
||||
// Limit to betweeen 1K and MAX_BLOCK_SIZE-1K for sanity:
|
||||
nBlockMaxSize = std::max((unsigned int)1000, std::min((unsigned int)(MAX_BLOCK_SIZE-1000), nBlockMaxSize));
|
||||
|
||||
// How much of the block should be dedicated to high-priority transactions,
|
||||
// included regardless of the fees they pay
|
||||
unsigned int nBlockPrioritySize = GetArg("-blockprioritysize", 27000);
|
||||
nBlockPrioritySize = std::min(nBlockMaxSize, nBlockPrioritySize);
|
||||
|
||||
// Minimum block size you want to create; block will be filled with free transactions
|
||||
// until there are no more or the block reaches this size:
|
||||
unsigned int nBlockMinSize = GetArg("-blockminsize", 0);
|
||||
nBlockMinSize = std::min(nBlockMaxSize, nBlockMinSize);
|
||||
|
||||
// Fee-per-kilobyte amount considered the same as "free"
|
||||
// Be careful setting this: if you set it to zero then
|
||||
// a transaction spammer can cheaply fill blocks using
|
||||
// 1-satoshi-fee transactions. It should be set above the real
|
||||
// cost to you of processing a transaction.
|
||||
int64 nMinTxFee = MIN_TX_FEE;
|
||||
if (mapArgs.count("-mintxfee"))
|
||||
ParseMoney(mapArgs["-mintxfee"], nMinTxFee);
|
||||
|
||||
// Collect memory pool transactions into the block
|
||||
int64 nFees = 0;
|
||||
{
|
||||
|
@ -3362,7 +3412,10 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
|||
// Priority order to process transactions
|
||||
list<COrphan> vOrphan; // list memory doesn't move
|
||||
map<uint256, vector<COrphan*> > mapDependers;
|
||||
multimap<double, CTransaction*> mapPriority;
|
||||
|
||||
// This vector will be sorted into a priority queue:
|
||||
vector<TxPriority> vecPriority;
|
||||
vecPriority.reserve(mempool.mapTx.size());
|
||||
for (map<uint256, CTransaction>::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi)
|
||||
{
|
||||
CTransaction& tx = (*mi).second;
|
||||
|
@ -3371,6 +3424,7 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
|||
|
||||
COrphan* porphan = NULL;
|
||||
double dPriority = 0;
|
||||
int64 nTotalIn = 0;
|
||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||
{
|
||||
// Read prev transaction
|
||||
|
@ -3387,34 +3441,32 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
|||
}
|
||||
mapDependers[txin.prevout.hash].push_back(porphan);
|
||||
porphan->setDependsOn.insert(txin.prevout.hash);
|
||||
nTotalIn += mempool.mapTx[txin.prevout.hash].vout[txin.prevout.n].nValue;
|
||||
continue;
|
||||
}
|
||||
int64 nValueIn = txPrev.vout[txin.prevout.n].nValue;
|
||||
nTotalIn += nValueIn;
|
||||
|
||||
// Read block header
|
||||
int nConf = txindex.GetDepthInMainChain();
|
||||
|
||||
dPriority += (double)nValueIn * nConf;
|
||||
|
||||
if (fDebug && GetBoolArg("-printpriority"))
|
||||
printf("priority nValueIn=%-12"PRI64d" nConf=%-5d dPriority=%-20.1f\n", nValueIn, nConf, dPriority);
|
||||
}
|
||||
|
||||
// Priority is sum(valuein * age) / txsize
|
||||
dPriority /= ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||
dPriority /= nTxSize;
|
||||
|
||||
// This is a more accurate fee-per-kilobyte than is used by the client code, because the
|
||||
// client code rounds up the size to the nearest 1K. That's good, because it gives an
|
||||
// incentive to create smaller transactions.
|
||||
double dFeePerKb = double(nTotalIn-tx.GetValueOut()) / (double(nTxSize)/1000.0);
|
||||
|
||||
if (porphan)
|
||||
porphan->dPriority = dPriority;
|
||||
else
|
||||
mapPriority.insert(make_pair(-dPriority, &(*mi).second));
|
||||
|
||||
if (fDebug && GetBoolArg("-printpriority"))
|
||||
{
|
||||
printf("priority %-20.1f %s\n%s", dPriority, tx.GetHash().ToString().substr(0,10).c_str(), tx.ToString().c_str());
|
||||
if (porphan)
|
||||
porphan->print();
|
||||
printf("\n");
|
||||
porphan->dPriority = dPriority;
|
||||
porphan->dFeePerKb = dFeePerKb;
|
||||
}
|
||||
else
|
||||
vecPriority.push_back(TxPriority(dPriority, dFeePerKb, &(*mi).second));
|
||||
}
|
||||
|
||||
// Collect transactions into block
|
||||
|
@ -3422,16 +3474,24 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
|||
uint64 nBlockSize = 1000;
|
||||
uint64 nBlockTx = 0;
|
||||
int nBlockSigOps = 100;
|
||||
while (!mapPriority.empty())
|
||||
bool fSortedByFee = (nBlockPrioritySize <= 0);
|
||||
|
||||
TxPriorityCompare comparer(fSortedByFee);
|
||||
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
||||
|
||||
while (!vecPriority.empty())
|
||||
{
|
||||
// Take highest priority transaction off priority queue
|
||||
double dPriority = -(*mapPriority.begin()).first;
|
||||
CTransaction& tx = *(*mapPriority.begin()).second;
|
||||
mapPriority.erase(mapPriority.begin());
|
||||
// Take highest priority transaction off the priority queue:
|
||||
double dPriority = vecPriority.front().get<0>();
|
||||
double dFeePerKb = vecPriority.front().get<1>();
|
||||
CTransaction& tx = *(vecPriority.front().get<2>());
|
||||
|
||||
std::pop_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
||||
vecPriority.pop_back();
|
||||
|
||||
// Size limits
|
||||
unsigned int nTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION);
|
||||
if (nBlockSize + nTxSize >= MAX_BLOCK_SIZE_GEN)
|
||||
if (nBlockSize + nTxSize >= nBlockMaxSize)
|
||||
continue;
|
||||
|
||||
// Legacy limits on sigOps:
|
||||
|
@ -3439,9 +3499,19 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
|||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||
continue;
|
||||
|
||||
// Transaction fee required depends on block size
|
||||
bool fAllowFree = (nBlockSize + nTxSize < 4000 || CTransaction::AllowFree(dPriority));
|
||||
int64 nMinFee = tx.GetMinFee(nBlockSize, fAllowFree, GMF_BLOCK);
|
||||
// Skip free transactions if we're past the minimum block size:
|
||||
if (fSortedByFee && (dFeePerKb < nMinTxFee) && (nBlockSize + nTxSize >= nBlockMinSize))
|
||||
continue;
|
||||
|
||||
// Prioritize by fee once past the priority size or we run out of high-priority
|
||||
// transactions:
|
||||
if (!fSortedByFee &&
|
||||
((nBlockSize + nTxSize >= nBlockPrioritySize) || (dPriority < COIN * 144 / 250)))
|
||||
{
|
||||
fSortedByFee = true;
|
||||
comparer = TxPriorityCompare(fSortedByFee);
|
||||
std::make_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
||||
}
|
||||
|
||||
// Connecting shouldn't fail due to dependency on other memory pool transactions
|
||||
// because we're already processing them in order of dependency
|
||||
|
@ -3452,8 +3522,6 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
|||
continue;
|
||||
|
||||
int64 nTxFees = tx.GetValueIn(mapInputs)-tx.GetValueOut();
|
||||
if (nTxFees < nMinFee)
|
||||
continue;
|
||||
|
||||
nTxSigOps += tx.GetP2SHSigOpCount(mapInputs);
|
||||
if (nBlockSigOps + nTxSigOps >= MAX_BLOCK_SIGOPS)
|
||||
|
@ -3471,6 +3539,12 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
|||
nBlockSigOps += nTxSigOps;
|
||||
nFees += nTxFees;
|
||||
|
||||
if (fDebug && GetBoolArg("-printpriority"))
|
||||
{
|
||||
printf("priority %.1f feeperkb %.1f txid %s\n",
|
||||
dPriority, dFeePerKb, tx.GetHash().ToString().c_str());
|
||||
}
|
||||
|
||||
// Add transactions that depend on this one to the priority queue
|
||||
uint256 hash = tx.GetHash();
|
||||
if (mapDependers.count(hash))
|
||||
|
@ -3481,7 +3555,10 @@ CBlock* CreateNewBlock(CReserveKey& reservekey)
|
|||
{
|
||||
porphan->setDependsOn.erase(hash);
|
||||
if (porphan->setDependsOn.empty())
|
||||
mapPriority.insert(make_pair(-porphan->dPriority, porphan->ptx));
|
||||
{
|
||||
vecPriority.push_back(TxPriority(porphan->dPriority, porphan->dFeePerKb, porphan->ptx));
|
||||
std::push_heap(vecPriority.begin(), vecPriority.end(), comparer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3654,7 +3731,8 @@ void static BitcoinMiner(CWallet *pwallet)
|
|||
return;
|
||||
IncrementExtraNonce(pblock.get(), pindexPrev, nExtraNonce);
|
||||
|
||||
printf("Running BitcoinMiner with %d transactions in block\n", pblock->vtx.size());
|
||||
printf("Running BitcoinMiner with %d transactions in block (%u bytes)\n", pblock->vtx.size(),
|
||||
::GetSerializeSize(*pblock, SER_NETWORK, PROTOCOL_VERSION));
|
||||
|
||||
|
||||
//
|
||||
|
|
Loading…
Reference in a new issue