Merge pull request #7062
901b01d
Remove GetMinRelayFee (Suhas Daftuar)27fae34
Use fee deltas for determining mempool acceptance (Suhas Daftuar)9ef2a25
Update replace-by-fee logic to use fee deltas (Suhas Daftuar)eb30666
Fix mempool limiting for PrioritiseTransaction (Suhas Daftuar)
This commit is contained in:
commit
c24337964f
8 changed files with 211 additions and 92 deletions
|
@ -64,17 +64,41 @@ class MempoolPackagesTest(BitcoinTestFramework):
|
||||||
for x in reversed(chain):
|
for x in reversed(chain):
|
||||||
assert_equal(mempool[x]['descendantcount'], descendant_count)
|
assert_equal(mempool[x]['descendantcount'], descendant_count)
|
||||||
descendant_fees += mempool[x]['fee']
|
descendant_fees += mempool[x]['fee']
|
||||||
|
assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee'])
|
||||||
assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees)
|
assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees)
|
||||||
descendant_size += mempool[x]['size']
|
descendant_size += mempool[x]['size']
|
||||||
assert_equal(mempool[x]['descendantsize'], descendant_size)
|
assert_equal(mempool[x]['descendantsize'], descendant_size)
|
||||||
descendant_count += 1
|
descendant_count += 1
|
||||||
|
|
||||||
|
# Check that descendant modified fees includes fee deltas from
|
||||||
|
# prioritisetransaction
|
||||||
|
self.nodes[0].prioritisetransaction(chain[-1], 0, 1000)
|
||||||
|
mempool = self.nodes[0].getrawmempool(True)
|
||||||
|
|
||||||
|
descendant_fees = 0
|
||||||
|
for x in reversed(chain):
|
||||||
|
descendant_fees += mempool[x]['fee']
|
||||||
|
assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees+1000)
|
||||||
|
|
||||||
# Adding one more transaction on to the chain should fail.
|
# Adding one more transaction on to the chain should fail.
|
||||||
try:
|
try:
|
||||||
self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
|
self.chain_transaction(self.nodes[0], txid, vout, value, fee, 1)
|
||||||
except JSONRPCException as e:
|
except JSONRPCException as e:
|
||||||
print "too-long-ancestor-chain successfully rejected"
|
print "too-long-ancestor-chain successfully rejected"
|
||||||
|
|
||||||
|
# Check that prioritising a tx before it's added to the mempool works
|
||||||
|
self.nodes[0].generate(1)
|
||||||
|
self.nodes[0].prioritisetransaction(chain[-1], 0, 2000)
|
||||||
|
self.nodes[0].invalidateblock(self.nodes[0].getbestblockhash())
|
||||||
|
mempool = self.nodes[0].getrawmempool(True)
|
||||||
|
|
||||||
|
descendant_fees = 0
|
||||||
|
for x in reversed(chain):
|
||||||
|
descendant_fees += mempool[x]['fee']
|
||||||
|
if (x == chain[-1]):
|
||||||
|
assert_equal(mempool[x]['modifiedfee'], mempool[x]['fee']+satoshi_round(0.00002))
|
||||||
|
assert_equal(mempool[x]['descendantfees'], SATOSHIS*descendant_fees+2000)
|
||||||
|
|
||||||
# TODO: check that node1's mempool is as expected
|
# TODO: check that node1's mempool is as expected
|
||||||
|
|
||||||
# TODO: test ancestor size limits
|
# TODO: test ancestor size limits
|
||||||
|
|
|
@ -143,5 +143,45 @@ class PrioritiseTransactionTest(BitcoinTestFramework):
|
||||||
if (x != high_fee_tx):
|
if (x != high_fee_tx):
|
||||||
assert(x not in mempool)
|
assert(x not in mempool)
|
||||||
|
|
||||||
|
# Create a free, low priority transaction. Should be rejected.
|
||||||
|
utxo_list = self.nodes[0].listunspent()
|
||||||
|
assert(len(utxo_list) > 0)
|
||||||
|
utxo = utxo_list[0]
|
||||||
|
|
||||||
|
inputs = []
|
||||||
|
outputs = {}
|
||||||
|
inputs.append({"txid" : utxo["txid"], "vout" : utxo["vout"]})
|
||||||
|
outputs[self.nodes[0].getnewaddress()] = utxo["amount"] - self.relayfee
|
||||||
|
raw_tx = self.nodes[0].createrawtransaction(inputs, outputs)
|
||||||
|
tx_hex = self.nodes[0].signrawtransaction(raw_tx)["hex"]
|
||||||
|
txid = self.nodes[0].sendrawtransaction(tx_hex)
|
||||||
|
|
||||||
|
# A tx that spends an in-mempool tx has 0 priority, so we can use it to
|
||||||
|
# test the effect of using prioritise transaction for mempool acceptance
|
||||||
|
inputs = []
|
||||||
|
inputs.append({"txid": txid, "vout": 0})
|
||||||
|
outputs = {}
|
||||||
|
outputs[self.nodes[0].getnewaddress()] = utxo["amount"] - self.relayfee
|
||||||
|
raw_tx2 = self.nodes[0].createrawtransaction(inputs, outputs)
|
||||||
|
tx2_hex = self.nodes[0].signrawtransaction(raw_tx2)["hex"]
|
||||||
|
tx2_id = self.nodes[0].decoderawtransaction(tx2_hex)["txid"]
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.nodes[0].sendrawtransaction(tx2_hex)
|
||||||
|
except JSONRPCException as exp:
|
||||||
|
assert_equal(exp.error['code'], -26) # insufficient fee
|
||||||
|
assert(tx2_id not in self.nodes[0].getrawmempool())
|
||||||
|
else:
|
||||||
|
assert(False)
|
||||||
|
|
||||||
|
# This is a less than 1000-byte transaction, so just set the fee
|
||||||
|
# to be the minimum for a 1000 byte transaction and check that it is
|
||||||
|
# accepted.
|
||||||
|
self.nodes[0].prioritisetransaction(tx2_id, 0, int(self.relayfee*COIN))
|
||||||
|
|
||||||
|
print "Assert that prioritised free transaction is accepted to mempool"
|
||||||
|
assert_equal(self.nodes[0].sendrawtransaction(tx2_hex), tx2_id)
|
||||||
|
assert(tx2_id in self.nodes[0].getrawmempool())
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
PrioritiseTransactionTest().main()
|
PrioritiseTransactionTest().main()
|
||||||
|
|
|
@ -63,8 +63,14 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
|
||||||
|
|
||||||
# If requested, ensure txouts are confirmed.
|
# If requested, ensure txouts are confirmed.
|
||||||
if confirmed:
|
if confirmed:
|
||||||
while len(node.getrawmempool()):
|
mempool_size = len(node.getrawmempool())
|
||||||
|
while mempool_size > 0:
|
||||||
node.generate(1)
|
node.generate(1)
|
||||||
|
new_size = len(node.getrawmempool())
|
||||||
|
# Error out if we have something stuck in the mempool, as this
|
||||||
|
# would likely be a bug.
|
||||||
|
assert(new_size < mempool_size)
|
||||||
|
mempool_size = new_size
|
||||||
|
|
||||||
return COutPoint(int(txid, 16), 0)
|
return COutPoint(int(txid, 16), 0)
|
||||||
|
|
||||||
|
@ -72,7 +78,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||||
|
|
||||||
def setup_network(self):
|
def setup_network(self):
|
||||||
self.nodes = []
|
self.nodes = []
|
||||||
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000",
|
self.nodes.append(start_node(0, self.options.tmpdir, ["-maxorphantx=1000", "-debug",
|
||||||
"-relaypriority=0", "-whitelist=127.0.0.1",
|
"-relaypriority=0", "-whitelist=127.0.0.1",
|
||||||
"-limitancestorcount=50",
|
"-limitancestorcount=50",
|
||||||
"-limitancestorsize=101",
|
"-limitancestorsize=101",
|
||||||
|
@ -108,6 +114,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||||
print "Running test opt-in..."
|
print "Running test opt-in..."
|
||||||
self.test_opt_in()
|
self.test_opt_in()
|
||||||
|
|
||||||
|
print "Running test prioritised transactions..."
|
||||||
|
self.test_prioritised_transactions()
|
||||||
|
|
||||||
print "Passed\n"
|
print "Passed\n"
|
||||||
|
|
||||||
def test_simple_doublespend(self):
|
def test_simple_doublespend(self):
|
||||||
|
@ -513,5 +522,72 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
||||||
# but make sure it is accepted anyway
|
# but make sure it is accepted anyway
|
||||||
self.nodes[0].sendrawtransaction(tx3c_hex, True)
|
self.nodes[0].sendrawtransaction(tx3c_hex, True)
|
||||||
|
|
||||||
|
def test_prioritised_transactions(self):
|
||||||
|
# Ensure that fee deltas used via prioritisetransaction are
|
||||||
|
# correctly used by replacement logic
|
||||||
|
|
||||||
|
# 1. Check that feeperkb uses modified fees
|
||||||
|
tx0_outpoint = make_utxo(self.nodes[0], 1.1*COIN)
|
||||||
|
|
||||||
|
tx1a = CTransaction()
|
||||||
|
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
|
||||||
|
tx1a.vout = [CTxOut(1*COIN, CScript([b'a']))]
|
||||||
|
tx1a_hex = txToHex(tx1a)
|
||||||
|
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
|
||||||
|
|
||||||
|
# Higher fee, but the actual fee per KB is much lower.
|
||||||
|
tx1b = CTransaction()
|
||||||
|
tx1b.vin = [CTxIn(tx0_outpoint, nSequence=0)]
|
||||||
|
tx1b.vout = [CTxOut(0.001*COIN, CScript([b'a'*740000]))]
|
||||||
|
tx1b_hex = txToHex(tx1b)
|
||||||
|
|
||||||
|
# Verify tx1b cannot replace tx1a.
|
||||||
|
try:
|
||||||
|
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
|
||||||
|
except JSONRPCException as exp:
|
||||||
|
assert_equal(exp.error['code'], -26)
|
||||||
|
else:
|
||||||
|
assert(False)
|
||||||
|
|
||||||
|
# Use prioritisetransaction to set tx1a's fee to 0.
|
||||||
|
self.nodes[0].prioritisetransaction(tx1a_txid, 0, int(-0.1*COIN))
|
||||||
|
|
||||||
|
# Now tx1b should be able to replace tx1a
|
||||||
|
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
|
||||||
|
|
||||||
|
assert(tx1b_txid in self.nodes[0].getrawmempool())
|
||||||
|
|
||||||
|
# 2. Check that absolute fee checks use modified fee.
|
||||||
|
tx1_outpoint = make_utxo(self.nodes[0], 1.1*COIN)
|
||||||
|
|
||||||
|
tx2a = CTransaction()
|
||||||
|
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)]
|
||||||
|
tx2a.vout = [CTxOut(1*COIN, CScript([b'a']))]
|
||||||
|
tx2a_hex = txToHex(tx2a)
|
||||||
|
tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True)
|
||||||
|
|
||||||
|
# Lower fee, but we'll prioritise it
|
||||||
|
tx2b = CTransaction()
|
||||||
|
tx2b.vin = [CTxIn(tx1_outpoint, nSequence=0)]
|
||||||
|
tx2b.vout = [CTxOut(1.01*COIN, CScript([b'a']))]
|
||||||
|
tx2b.rehash()
|
||||||
|
tx2b_hex = txToHex(tx2b)
|
||||||
|
|
||||||
|
# Verify tx2b cannot replace tx2a.
|
||||||
|
try:
|
||||||
|
tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True)
|
||||||
|
except JSONRPCException as exp:
|
||||||
|
assert_equal(exp.error['code'], -26)
|
||||||
|
else:
|
||||||
|
assert(False)
|
||||||
|
|
||||||
|
# Now prioritise tx2b to have a higher modified fee
|
||||||
|
self.nodes[0].prioritisetransaction(tx2b.hash, 0, int(0.1*COIN))
|
||||||
|
|
||||||
|
# tx2b should now be accepted
|
||||||
|
tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True)
|
||||||
|
|
||||||
|
assert(tx2b_txid in self.nodes[0].getrawmempool())
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
ReplaceByFeeTest().main()
|
ReplaceByFeeTest().main()
|
||||||
|
|
57
src/main.cpp
57
src/main.cpp
|
@ -800,32 +800,6 @@ void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age) {
|
||||||
pcoinsTip->Uncache(removed);
|
pcoinsTip->Uncache(removed);
|
||||||
}
|
}
|
||||||
|
|
||||||
CAmount GetMinRelayFee(const CTransaction& tx, const CTxMemPool& pool, unsigned int nBytes, bool fAllowFree)
|
|
||||||
{
|
|
||||||
uint256 hash = tx.GetHash();
|
|
||||||
double dPriorityDelta = 0;
|
|
||||||
CAmount nFeeDelta = 0;
|
|
||||||
pool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
|
|
||||||
if (dPriorityDelta > 0 || nFeeDelta > 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes);
|
|
||||||
|
|
||||||
if (fAllowFree)
|
|
||||||
{
|
|
||||||
// There is a free transaction area in blocks created by most miners,
|
|
||||||
// * If we are relaying we allow transactions up to DEFAULT_BLOCK_PRIORITY_SIZE - 1000
|
|
||||||
// to be considered to fall into this category. We don't want to encourage sending
|
|
||||||
// multiple transactions instead of one big transaction to avoid fees.
|
|
||||||
if (nBytes < (DEFAULT_BLOCK_PRIORITY_SIZE - 1000))
|
|
||||||
nMinFee = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!MoneyRange(nMinFee))
|
|
||||||
nMinFee = MAX_MONEY;
|
|
||||||
return nMinFee;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Convert CValidationState to a human-readable message for logging */
|
/** Convert CValidationState to a human-readable message for logging */
|
||||||
std::string FormatStateMessage(const CValidationState &state)
|
std::string FormatStateMessage(const CValidationState &state)
|
||||||
{
|
{
|
||||||
|
@ -968,6 +942,11 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
||||||
|
|
||||||
CAmount nValueOut = tx.GetValueOut();
|
CAmount nValueOut = tx.GetValueOut();
|
||||||
CAmount nFees = nValueIn-nValueOut;
|
CAmount nFees = nValueIn-nValueOut;
|
||||||
|
// nModifiedFees includes any fee deltas from PrioritiseTransaction
|
||||||
|
CAmount nModifiedFees = nFees;
|
||||||
|
double nPriorityDummy = 0;
|
||||||
|
pool.ApplyDeltas(hash, nPriorityDummy, nModifiedFees);
|
||||||
|
|
||||||
CAmount inChainInputValue;
|
CAmount inChainInputValue;
|
||||||
double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue);
|
double dPriority = view.GetPriority(tx, chainActive.Height(), inChainInputValue);
|
||||||
|
|
||||||
|
@ -985,16 +964,10 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
||||||
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps);
|
CTxMemPoolEntry entry(tx, nFees, GetTime(), dPriority, chainActive.Height(), pool.HasNoInputsOf(tx), inChainInputValue, fSpendsCoinbase, nSigOps);
|
||||||
unsigned int nSize = entry.GetTxSize();
|
unsigned int nSize = entry.GetTxSize();
|
||||||
|
|
||||||
// Don't accept it if it can't get into a block
|
|
||||||
CAmount txMinFee = GetMinRelayFee(tx, pool, nSize, true);
|
|
||||||
if (fLimitFree && nFees < txMinFee)
|
|
||||||
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient fee", false,
|
|
||||||
strprintf("%d < %d", nFees, txMinFee));
|
|
||||||
|
|
||||||
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
|
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
|
||||||
if (mempoolRejectFee > 0 && nFees < mempoolRejectFee) {
|
if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
|
||||||
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
|
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
|
||||||
} else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) {
|
} else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nModifiedFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) {
|
||||||
// Require that free transactions have sufficient priority to be mined in the next block.
|
// Require that free transactions have sufficient priority to be mined in the next block.
|
||||||
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
|
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
|
||||||
}
|
}
|
||||||
|
@ -1002,7 +975,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
||||||
// Continuously rate-limit free (really, very-low-fee) transactions
|
// Continuously rate-limit free (really, very-low-fee) transactions
|
||||||
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
|
// This mitigates 'penny-flooding' -- sending thousands of free transactions just to
|
||||||
// be annoying or make others' transactions take longer to confirm.
|
// be annoying or make others' transactions take longer to confirm.
|
||||||
if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize))
|
if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize))
|
||||||
{
|
{
|
||||||
static CCriticalSection csFreeLimiter;
|
static CCriticalSection csFreeLimiter;
|
||||||
static double dFreeCount;
|
static double dFreeCount;
|
||||||
|
@ -1067,7 +1040,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
||||||
LOCK(pool.cs);
|
LOCK(pool.cs);
|
||||||
if (setConflicts.size())
|
if (setConflicts.size())
|
||||||
{
|
{
|
||||||
CFeeRate newFeeRate(nFees, nSize);
|
CFeeRate newFeeRate(nModifiedFees, nSize);
|
||||||
set<uint256> setConflictsParents;
|
set<uint256> setConflictsParents;
|
||||||
const int maxDescendantsToVisit = 100;
|
const int maxDescendantsToVisit = 100;
|
||||||
CTxMemPool::setEntries setIterConflicting;
|
CTxMemPool::setEntries setIterConflicting;
|
||||||
|
@ -1110,7 +1083,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
||||||
// ignored when deciding whether or not to replace, we do
|
// ignored when deciding whether or not to replace, we do
|
||||||
// require the replacement to pay more overall fees too,
|
// require the replacement to pay more overall fees too,
|
||||||
// mitigating most cases.
|
// mitigating most cases.
|
||||||
CFeeRate oldFeeRate(mi->GetFee(), mi->GetTxSize());
|
CFeeRate oldFeeRate(mi->GetModifiedFee(), mi->GetTxSize());
|
||||||
if (newFeeRate <= oldFeeRate)
|
if (newFeeRate <= oldFeeRate)
|
||||||
{
|
{
|
||||||
return state.DoS(0,
|
return state.DoS(0,
|
||||||
|
@ -1138,7 +1111,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
||||||
pool.CalculateDescendants(it, allConflicting);
|
pool.CalculateDescendants(it, allConflicting);
|
||||||
}
|
}
|
||||||
BOOST_FOREACH(CTxMemPool::txiter it, allConflicting) {
|
BOOST_FOREACH(CTxMemPool::txiter it, allConflicting) {
|
||||||
nConflictingFees += it->GetFee();
|
nConflictingFees += it->GetModifiedFee();
|
||||||
nConflictingSize += it->GetTxSize();
|
nConflictingSize += it->GetTxSize();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1171,16 +1144,16 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
||||||
// The replacement must pay greater fees than the transactions it
|
// The replacement must pay greater fees than the transactions it
|
||||||
// replaces - if we did the bandwidth used by those conflicting
|
// replaces - if we did the bandwidth used by those conflicting
|
||||||
// transactions would not be paid for.
|
// transactions would not be paid for.
|
||||||
if (nFees < nConflictingFees)
|
if (nModifiedFees < nConflictingFees)
|
||||||
{
|
{
|
||||||
return state.DoS(0, error("AcceptToMemoryPool: rejecting replacement %s, less fees than conflicting txs; %s < %s",
|
return state.DoS(0, error("AcceptToMemoryPool: rejecting replacement %s, less fees than conflicting txs; %s < %s",
|
||||||
hash.ToString(), FormatMoney(nFees), FormatMoney(nConflictingFees)),
|
hash.ToString(), FormatMoney(nModifiedFees), FormatMoney(nConflictingFees)),
|
||||||
REJECT_INSUFFICIENTFEE, "insufficient fee");
|
REJECT_INSUFFICIENTFEE, "insufficient fee");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Finally in addition to paying more fees than the conflicts the
|
// Finally in addition to paying more fees than the conflicts the
|
||||||
// new transaction must pay for its own bandwidth.
|
// new transaction must pay for its own bandwidth.
|
||||||
CAmount nDeltaFees = nFees - nConflictingFees;
|
CAmount nDeltaFees = nModifiedFees - nConflictingFees;
|
||||||
if (nDeltaFees < ::minRelayTxFee.GetFee(nSize))
|
if (nDeltaFees < ::minRelayTxFee.GetFee(nSize))
|
||||||
{
|
{
|
||||||
return state.DoS(0,
|
return state.DoS(0,
|
||||||
|
@ -1218,7 +1191,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState &state, const C
|
||||||
LogPrint("mempool", "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n",
|
LogPrint("mempool", "replacing tx %s with %s for %s BTC additional fees, %d delta bytes\n",
|
||||||
it->GetTx().GetHash().ToString(),
|
it->GetTx().GetHash().ToString(),
|
||||||
hash.ToString(),
|
hash.ToString(),
|
||||||
FormatMoney(nFees - nConflictingFees),
|
FormatMoney(nModifiedFees - nConflictingFees),
|
||||||
(int)nSize - (int)nConflictingSize);
|
(int)nSize - (int)nConflictingSize);
|
||||||
}
|
}
|
||||||
pool.RemoveStaged(allConflicting);
|
pool.RemoveStaged(allConflicting);
|
||||||
|
|
|
@ -300,8 +300,6 @@ struct CDiskTxPos : public CDiskBlockPos
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Count ECDSA signature operations the old-fashioned (pre-0.6) way
|
* Count ECDSA signature operations the old-fashioned (pre-0.6) way
|
||||||
* @return number of sigops this transaction's outputs will produce when spent
|
* @return number of sigops this transaction's outputs will produce when spent
|
||||||
|
|
|
@ -197,7 +197,7 @@ UniValue mempoolToJSON(bool fVerbose = false)
|
||||||
info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height())));
|
info.push_back(Pair("currentpriority", e.GetPriority(chainActive.Height())));
|
||||||
info.push_back(Pair("descendantcount", e.GetCountWithDescendants()));
|
info.push_back(Pair("descendantcount", e.GetCountWithDescendants()));
|
||||||
info.push_back(Pair("descendantsize", e.GetSizeWithDescendants()));
|
info.push_back(Pair("descendantsize", e.GetSizeWithDescendants()));
|
||||||
info.push_back(Pair("descendantfees", e.GetFeesWithDescendants()));
|
info.push_back(Pair("descendantfees", e.GetModFeesWithDescendants()));
|
||||||
const CTransaction& tx = e.GetTx();
|
const CTransaction& tx = e.GetTx();
|
||||||
set<string> setDepends;
|
set<string> setDepends;
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||||
|
@ -255,7 +255,7 @@ UniValue getrawmempool(const UniValue& params, bool fHelp)
|
||||||
" \"currentpriority\" : n, (numeric) transaction priority now\n"
|
" \"currentpriority\" : n, (numeric) transaction priority now\n"
|
||||||
" \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n"
|
" \"descendantcount\" : n, (numeric) number of in-mempool descendant transactions (including this one)\n"
|
||||||
" \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n"
|
" \"descendantsize\" : n, (numeric) size of in-mempool descendants (including this one)\n"
|
||||||
" \"descendantfees\" : n, (numeric) fees of in-mempool descendants (including this one)\n"
|
" \"descendantfees\" : n, (numeric) modified fees (see above) of in-mempool descendants (including this one)\n"
|
||||||
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
|
" \"depends\" : [ (array) unconfirmed transactions used as inputs for this transaction\n"
|
||||||
" \"transactionid\", (string) parent transaction id\n"
|
" \"transactionid\", (string) parent transaction id\n"
|
||||||
" ... ]\n"
|
" ... ]\n"
|
||||||
|
|
|
@ -33,7 +33,7 @@ CTxMemPoolEntry::CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||||
|
|
||||||
nCountWithDescendants = 1;
|
nCountWithDescendants = 1;
|
||||||
nSizeWithDescendants = nTxSize;
|
nSizeWithDescendants = nTxSize;
|
||||||
nFeesWithDescendants = nFee;
|
nModFeesWithDescendants = nFee;
|
||||||
CAmount nValueIn = tx.GetValueOut()+nFee;
|
CAmount nValueIn = tx.GetValueOut()+nFee;
|
||||||
assert(inChainInputValue <= nValueIn);
|
assert(inChainInputValue <= nValueIn);
|
||||||
|
|
||||||
|
@ -57,6 +57,7 @@ CTxMemPoolEntry::GetPriority(unsigned int currentHeight) const
|
||||||
|
|
||||||
void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta)
|
void CTxMemPoolEntry::UpdateFeeDelta(int64_t newFeeDelta)
|
||||||
{
|
{
|
||||||
|
nModFeesWithDescendants += newFeeDelta - feeDelta;
|
||||||
feeDelta = newFeeDelta;
|
feeDelta = newFeeDelta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,7 +115,7 @@ bool CTxMemPool::UpdateForDescendants(txiter updateIt, int maxDescendantsToVisit
|
||||||
BOOST_FOREACH(txiter cit, setAllDescendants) {
|
BOOST_FOREACH(txiter cit, setAllDescendants) {
|
||||||
if (!setExclude.count(cit->GetTx().GetHash())) {
|
if (!setExclude.count(cit->GetTx().GetHash())) {
|
||||||
modifySize += cit->GetTxSize();
|
modifySize += cit->GetTxSize();
|
||||||
modifyFee += cit->GetFee();
|
modifyFee += cit->GetModifiedFee();
|
||||||
modifyCount++;
|
modifyCount++;
|
||||||
cachedDescendants[updateIt].insert(cit);
|
cachedDescendants[updateIt].insert(cit);
|
||||||
}
|
}
|
||||||
|
@ -244,7 +245,7 @@ void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors
|
||||||
}
|
}
|
||||||
const int64_t updateCount = (add ? 1 : -1);
|
const int64_t updateCount = (add ? 1 : -1);
|
||||||
const int64_t updateSize = updateCount * it->GetTxSize();
|
const int64_t updateSize = updateCount * it->GetTxSize();
|
||||||
const CAmount updateFee = updateCount * it->GetFee();
|
const CAmount updateFee = updateCount * it->GetModifiedFee();
|
||||||
BOOST_FOREACH(txiter ancestorIt, setAncestors) {
|
BOOST_FOREACH(txiter ancestorIt, setAncestors) {
|
||||||
mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, updateCount));
|
mapTx.modify(ancestorIt, update_descendant_state(updateSize, updateFee, updateCount));
|
||||||
}
|
}
|
||||||
|
@ -304,7 +305,7 @@ void CTxMemPoolEntry::SetDirty()
|
||||||
{
|
{
|
||||||
nCountWithDescendants = 0;
|
nCountWithDescendants = 0;
|
||||||
nSizeWithDescendants = nTxSize;
|
nSizeWithDescendants = nTxSize;
|
||||||
nFeesWithDescendants = nFee;
|
nModFeesWithDescendants = GetModifiedFee();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount)
|
void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount)
|
||||||
|
@ -312,8 +313,7 @@ void CTxMemPoolEntry::UpdateState(int64_t modifySize, CAmount modifyFee, int64_t
|
||||||
if (!IsDirty()) {
|
if (!IsDirty()) {
|
||||||
nSizeWithDescendants += modifySize;
|
nSizeWithDescendants += modifySize;
|
||||||
assert(int64_t(nSizeWithDescendants) > 0);
|
assert(int64_t(nSizeWithDescendants) > 0);
|
||||||
nFeesWithDescendants += modifyFee;
|
nModFeesWithDescendants += modifyFee;
|
||||||
assert(nFeesWithDescendants >= 0);
|
|
||||||
nCountWithDescendants += modifyCount;
|
nCountWithDescendants += modifyCount;
|
||||||
assert(int64_t(nCountWithDescendants) > 0);
|
assert(int64_t(nCountWithDescendants) > 0);
|
||||||
}
|
}
|
||||||
|
@ -372,6 +372,17 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||||
indexed_transaction_set::iterator newit = mapTx.insert(entry).first;
|
indexed_transaction_set::iterator newit = mapTx.insert(entry).first;
|
||||||
mapLinks.insert(make_pair(newit, TxLinks()));
|
mapLinks.insert(make_pair(newit, TxLinks()));
|
||||||
|
|
||||||
|
// Update transaction for any feeDelta created by PrioritiseTransaction
|
||||||
|
// TODO: refactor so that the fee delta is calculated before inserting
|
||||||
|
// into mapTx.
|
||||||
|
std::map<uint256, std::pair<double, CAmount> >::const_iterator pos = mapDeltas.find(hash);
|
||||||
|
if (pos != mapDeltas.end()) {
|
||||||
|
const std::pair<double, CAmount> &deltas = pos->second;
|
||||||
|
if (deltas.second) {
|
||||||
|
mapTx.modify(newit, update_fee_delta(deltas.second));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update cachedInnerUsage to include contained transaction's usage.
|
// Update cachedInnerUsage to include contained transaction's usage.
|
||||||
// (When we update the entry for in-mempool parents, memory usage will be
|
// (When we update the entry for in-mempool parents, memory usage will be
|
||||||
// further updated.)
|
// further updated.)
|
||||||
|
@ -399,15 +410,6 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry,
|
||||||
}
|
}
|
||||||
UpdateAncestorsOf(true, newit, setAncestors);
|
UpdateAncestorsOf(true, newit, setAncestors);
|
||||||
|
|
||||||
// Update transaction's score for any feeDelta created by PrioritiseTransaction
|
|
||||||
std::map<uint256, std::pair<double, CAmount> >::const_iterator pos = mapDeltas.find(hash);
|
|
||||||
if (pos != mapDeltas.end()) {
|
|
||||||
const std::pair<double, CAmount> &deltas = pos->second;
|
|
||||||
if (deltas.second) {
|
|
||||||
mapTx.modify(newit, update_fee_delta(deltas.second));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nTransactionsUpdated++;
|
nTransactionsUpdated++;
|
||||||
totalTxSize += entry.GetTxSize();
|
totalTxSize += entry.GetTxSize();
|
||||||
minerPolicyEstimator->processTransaction(entry, fCurrentEstimate);
|
minerPolicyEstimator->processTransaction(entry, fCurrentEstimate);
|
||||||
|
@ -644,27 +646,24 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const
|
||||||
CTxMemPool::setEntries setChildrenCheck;
|
CTxMemPool::setEntries setChildrenCheck;
|
||||||
std::map<COutPoint, CInPoint>::const_iterator iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0));
|
std::map<COutPoint, CInPoint>::const_iterator iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0));
|
||||||
int64_t childSizes = 0;
|
int64_t childSizes = 0;
|
||||||
CAmount childFees = 0;
|
CAmount childModFee = 0;
|
||||||
for (; iter != mapNextTx.end() && iter->first.hash == it->GetTx().GetHash(); ++iter) {
|
for (; iter != mapNextTx.end() && iter->first.hash == it->GetTx().GetHash(); ++iter) {
|
||||||
txiter childit = mapTx.find(iter->second.ptx->GetHash());
|
txiter childit = mapTx.find(iter->second.ptx->GetHash());
|
||||||
assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions
|
assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions
|
||||||
if (setChildrenCheck.insert(childit).second) {
|
if (setChildrenCheck.insert(childit).second) {
|
||||||
childSizes += childit->GetTxSize();
|
childSizes += childit->GetTxSize();
|
||||||
childFees += childit->GetFee();
|
childModFee += childit->GetModifiedFee();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(setChildrenCheck == GetMemPoolChildren(it));
|
assert(setChildrenCheck == GetMemPoolChildren(it));
|
||||||
// Also check to make sure size/fees is greater than sum with immediate children.
|
// Also check to make sure size is greater than sum with immediate children.
|
||||||
// just a sanity check, not definitive that this calc is correct...
|
// just a sanity check, not definitive that this calc is correct...
|
||||||
// also check that the size is less than the size of the entire mempool.
|
|
||||||
if (!it->IsDirty()) {
|
if (!it->IsDirty()) {
|
||||||
assert(it->GetSizeWithDescendants() >= childSizes + it->GetTxSize());
|
assert(it->GetSizeWithDescendants() >= childSizes + it->GetTxSize());
|
||||||
assert(it->GetFeesWithDescendants() >= childFees + it->GetFee());
|
|
||||||
} else {
|
} else {
|
||||||
assert(it->GetSizeWithDescendants() == it->GetTxSize());
|
assert(it->GetSizeWithDescendants() == it->GetTxSize());
|
||||||
assert(it->GetFeesWithDescendants() == it->GetFee());
|
assert(it->GetModFeesWithDescendants() == it->GetModifiedFee());
|
||||||
}
|
}
|
||||||
assert(it->GetFeesWithDescendants() >= 0);
|
|
||||||
|
|
||||||
if (fDependsWait)
|
if (fDependsWait)
|
||||||
waitingOnDependants.push_back(&(*it));
|
waitingOnDependants.push_back(&(*it));
|
||||||
|
@ -788,6 +787,14 @@ void CTxMemPool::PrioritiseTransaction(const uint256 hash, const string strHash,
|
||||||
txiter it = mapTx.find(hash);
|
txiter it = mapTx.find(hash);
|
||||||
if (it != mapTx.end()) {
|
if (it != mapTx.end()) {
|
||||||
mapTx.modify(it, update_fee_delta(deltas.second));
|
mapTx.modify(it, update_fee_delta(deltas.second));
|
||||||
|
// Now update all ancestors' modified fees with descendants
|
||||||
|
setEntries setAncestors;
|
||||||
|
uint64_t nNoLimit = std::numeric_limits<uint64_t>::max();
|
||||||
|
std::string dummy;
|
||||||
|
CalculateMemPoolAncestors(*it, setAncestors, nNoLimit, nNoLimit, nNoLimit, nNoLimit, dummy, false);
|
||||||
|
BOOST_FOREACH(txiter ancestorIt, setAncestors) {
|
||||||
|
mapTx.modify(ancestorIt, update_descendant_state(0, nFeeDelta, 0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta));
|
LogPrintf("PrioritiseTransaction: %s priority += %f, fee += %d\n", strHash, dPriorityDelta, FormatMoney(nFeeDelta));
|
||||||
|
@ -956,7 +963,7 @@ void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<uint256>* pvNoSpendsRe
|
||||||
// "minimum reasonable fee rate" (ie some value under which we consider txn
|
// "minimum reasonable fee rate" (ie some value under which we consider txn
|
||||||
// to have 0 fee). This way, we don't allow txn to enter mempool with feerate
|
// to have 0 fee). This way, we don't allow txn to enter mempool with feerate
|
||||||
// equal to txn which were removed with no block in between.
|
// equal to txn which were removed with no block in between.
|
||||||
CFeeRate removed(it->GetFeesWithDescendants(), it->GetSizeWithDescendants());
|
CFeeRate removed(it->GetModFeesWithDescendants(), it->GetSizeWithDescendants());
|
||||||
removed += minReasonableRelayFee;
|
removed += minReasonableRelayFee;
|
||||||
trackPackageRemoved(removed);
|
trackPackageRemoved(removed);
|
||||||
maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed);
|
maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed);
|
||||||
|
|
|
@ -44,12 +44,12 @@ class CTxMemPool;
|
||||||
* ("descendant" transactions).
|
* ("descendant" transactions).
|
||||||
*
|
*
|
||||||
* When a new entry is added to the mempool, we update the descendant state
|
* When a new entry is added to the mempool, we update the descendant state
|
||||||
* (nCountWithDescendants, nSizeWithDescendants, and nFeesWithDescendants) for
|
* (nCountWithDescendants, nSizeWithDescendants, and nModFeesWithDescendants) for
|
||||||
* all ancestors of the newly added transaction.
|
* all ancestors of the newly added transaction.
|
||||||
*
|
*
|
||||||
* If updating the descendant state is skipped, we can mark the entry as
|
* If updating the descendant state is skipped, we can mark the entry as
|
||||||
* "dirty", and set nSizeWithDescendants/nFeesWithDescendants to equal nTxSize/
|
* "dirty", and set nSizeWithDescendants/nModFeesWithDescendants to equal nTxSize/
|
||||||
* nTxFee. (This can potentially happen during a reorg, where we limit the
|
* nFee+feeDelta. (This can potentially happen during a reorg, where we limit the
|
||||||
* amount of work we're willing to do to avoid consuming too much CPU.)
|
* amount of work we're willing to do to avoid consuming too much CPU.)
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
@ -74,11 +74,11 @@ private:
|
||||||
// Information about descendants of this transaction that are in the
|
// Information about descendants of this transaction that are in the
|
||||||
// mempool; if we remove this transaction we must remove all of these
|
// mempool; if we remove this transaction we must remove all of these
|
||||||
// descendants as well. if nCountWithDescendants is 0, treat this entry as
|
// descendants as well. if nCountWithDescendants is 0, treat this entry as
|
||||||
// dirty, and nSizeWithDescendants and nFeesWithDescendants will not be
|
// dirty, and nSizeWithDescendants and nModFeesWithDescendants will not be
|
||||||
// correct.
|
// correct.
|
||||||
uint64_t nCountWithDescendants; //! number of descendant transactions
|
uint64_t nCountWithDescendants; //! number of descendant transactions
|
||||||
uint64_t nSizeWithDescendants; //! ... and size
|
uint64_t nSizeWithDescendants; //! ... and size
|
||||||
CAmount nFeesWithDescendants; //! ... and total fees (all including us)
|
CAmount nModFeesWithDescendants; //! ... and total fees (all including us)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
CTxMemPoolEntry(const CTransaction& _tx, const CAmount& _nFee,
|
||||||
|
@ -104,7 +104,8 @@ public:
|
||||||
|
|
||||||
// Adjusts the descendant state, if this entry is not dirty.
|
// Adjusts the descendant state, if this entry is not dirty.
|
||||||
void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
|
void UpdateState(int64_t modifySize, CAmount modifyFee, int64_t modifyCount);
|
||||||
// Updates the fee delta used for mining priority score
|
// Updates the fee delta used for mining priority score, and the
|
||||||
|
// modified fees with descendants.
|
||||||
void UpdateFeeDelta(int64_t feeDelta);
|
void UpdateFeeDelta(int64_t feeDelta);
|
||||||
|
|
||||||
/** We can set the entry to be dirty if doing the full calculation of in-
|
/** We can set the entry to be dirty if doing the full calculation of in-
|
||||||
|
@ -116,7 +117,7 @@ public:
|
||||||
|
|
||||||
uint64_t GetCountWithDescendants() const { return nCountWithDescendants; }
|
uint64_t GetCountWithDescendants() const { return nCountWithDescendants; }
|
||||||
uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; }
|
uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; }
|
||||||
CAmount GetFeesWithDescendants() const { return nFeesWithDescendants; }
|
CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; }
|
||||||
|
|
||||||
bool GetSpendsCoinbase() const { return spendsCoinbase; }
|
bool GetSpendsCoinbase() const { return spendsCoinbase; }
|
||||||
};
|
};
|
||||||
|
@ -163,27 +164,27 @@ struct mempoolentry_txid
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** \class CompareTxMemPoolEntryByFee
|
/** \class CompareTxMemPoolEntryByDescendantScore
|
||||||
*
|
*
|
||||||
* Sort an entry by max(feerate of entry's tx, feerate with all descendants).
|
* Sort an entry by max(score/size of entry's tx, score/size with all descendants).
|
||||||
*/
|
*/
|
||||||
class CompareTxMemPoolEntryByFee
|
class CompareTxMemPoolEntryByDescendantScore
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b)
|
bool operator()(const CTxMemPoolEntry& a, const CTxMemPoolEntry& b)
|
||||||
{
|
{
|
||||||
bool fUseADescendants = UseDescendantFeeRate(a);
|
bool fUseADescendants = UseDescendantScore(a);
|
||||||
bool fUseBDescendants = UseDescendantFeeRate(b);
|
bool fUseBDescendants = UseDescendantScore(b);
|
||||||
|
|
||||||
double aFees = fUseADescendants ? a.GetFeesWithDescendants() : a.GetFee();
|
double aModFee = fUseADescendants ? a.GetModFeesWithDescendants() : a.GetModifiedFee();
|
||||||
double aSize = fUseADescendants ? a.GetSizeWithDescendants() : a.GetTxSize();
|
double aSize = fUseADescendants ? a.GetSizeWithDescendants() : a.GetTxSize();
|
||||||
|
|
||||||
double bFees = fUseBDescendants ? b.GetFeesWithDescendants() : b.GetFee();
|
double bModFee = fUseBDescendants ? b.GetModFeesWithDescendants() : b.GetModifiedFee();
|
||||||
double bSize = fUseBDescendants ? b.GetSizeWithDescendants() : b.GetTxSize();
|
double bSize = fUseBDescendants ? b.GetSizeWithDescendants() : b.GetTxSize();
|
||||||
|
|
||||||
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
|
// Avoid division by rewriting (a/b > c/d) as (a*d > c*b).
|
||||||
double f1 = aFees * bSize;
|
double f1 = aModFee * bSize;
|
||||||
double f2 = aSize * bFees;
|
double f2 = aSize * bModFee;
|
||||||
|
|
||||||
if (f1 == f2) {
|
if (f1 == f2) {
|
||||||
return a.GetTime() >= b.GetTime();
|
return a.GetTime() >= b.GetTime();
|
||||||
|
@ -191,11 +192,11 @@ public:
|
||||||
return f1 < f2;
|
return f1 < f2;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate which feerate to use for an entry (avoiding division).
|
// Calculate which score to use for an entry (avoiding division).
|
||||||
bool UseDescendantFeeRate(const CTxMemPoolEntry &a)
|
bool UseDescendantScore(const CTxMemPoolEntry &a)
|
||||||
{
|
{
|
||||||
double f1 = (double)a.GetFee() * a.GetSizeWithDescendants();
|
double f1 = (double)a.GetModifiedFee() * a.GetSizeWithDescendants();
|
||||||
double f2 = (double)a.GetFeesWithDescendants() * a.GetTxSize();
|
double f2 = (double)a.GetModFeesWithDescendants() * a.GetTxSize();
|
||||||
return f2 > f1;
|
return f2 > f1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -350,7 +351,7 @@ public:
|
||||||
// sorted by fee rate
|
// sorted by fee rate
|
||||||
boost::multi_index::ordered_non_unique<
|
boost::multi_index::ordered_non_unique<
|
||||||
boost::multi_index::identity<CTxMemPoolEntry>,
|
boost::multi_index::identity<CTxMemPoolEntry>,
|
||||||
CompareTxMemPoolEntryByFee
|
CompareTxMemPoolEntryByDescendantScore
|
||||||
>,
|
>,
|
||||||
// sorted by entry time
|
// sorted by entry time
|
||||||
boost::multi_index::ordered_non_unique<
|
boost::multi_index::ordered_non_unique<
|
||||||
|
|
Loading…
Reference in a new issue