diff --git a/src/miner.cpp b/src/miner.cpp --- a/src/miner.cpp +++ b/src/miner.cpp @@ -511,15 +511,18 @@ assert(!inBlock.count(iter)); uint64_t packageSize = iter->GetSizeWithAncestors(); + uint64_t packageBillableSize = iter->GetBillableSizeWithAncestors(); + Amount packageFees = iter->GetModFeesWithAncestors(); int64_t packageSigOps = iter->GetSigOpCountWithAncestors(); if (fUsingModified) { packageSize = modit->nSizeWithAncestors; + packageBillableSize = modit->nBillableSizeWithAncestors; packageFees = modit->nModFeesWithAncestors; packageSigOps = modit->nSigOpCountWithAncestors; } - if (packageFees < blockMinFeeRate.GetFee(packageSize)) { + if (packageFees < blockMinFeeRate.GetFee(packageBillableSize)) { // Everything else we might consider has a lower fee rate return; } diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -40,6 +40,7 @@ Amount totalFee = Amount::zero(); size_t totalSize = CTransaction(parentOfAll).GetTotalSize(); + size_t totalBillableSize = CTransaction(parentOfAll).GetBillableSize(); // Generate 100 transactions for (size_t totalTransactions = 0; totalTransactions < 100; @@ -52,6 +53,8 @@ Amount maxFees = Amount::zero(); uint64_t minSize = std::numeric_limits::max(); uint64_t maxSize = 0; + uint64_t minBillableSize = std::numeric_limits::max(); + uint64_t maxBillableSize = 0; // Consume random inputs, but make sure we don't consume more than // available for (size_t input = std::min(InsecureRandRange(maxOutputs) + 1, @@ -76,6 +79,9 @@ maxFees += parent.GetModFeesWithAncestors(); minSize = std::min(minSize, parent.GetSizeWithAncestors()); maxSize += parent.GetSizeWithAncestors(); + minBillableSize = std::min(minBillableSize, + parent.GetBillableSizeWithAncestors()); + maxBillableSize += parent.GetBillableSizeWithAncestors(); } // Produce random number of outputs @@ -103,10 +109,13 @@ maxFees += randFee; minSize += CTransaction(tx).GetTotalSize(); maxSize += CTransaction(tx).GetTotalSize(); + minBillableSize += CTransaction(tx).GetBillableSize(); + maxBillableSize += CTransaction(tx).GetBillableSize(); // Calculate overall values totalFee += randFee; totalSize += CTransaction(tx).GetTotalSize(); + totalBillableSize += CTransaction(tx).GetBillableSize(); CTxMemPoolEntry parentEntry = *testPool.mapTx.find(parentOfAllId); CTxMemPoolEntry latestEntry = *testPool.mapTx.find(curId); @@ -117,12 +126,19 @@ BOOST_CHECK(latestEntry.GetSizeWithAncestors() >= minSize); BOOST_CHECK(latestEntry.GetSizeWithAncestors() <= maxSize); + BOOST_CHECK(latestEntry.GetBillableSizeWithAncestors() >= + minBillableSize); + BOOST_CHECK(latestEntry.GetBillableSizeWithAncestors() <= + maxBillableSize); + BOOST_CHECK(latestEntry.GetModFeesWithAncestors() >= minFees); BOOST_CHECK(latestEntry.GetModFeesWithAncestors() <= maxFees); BOOST_CHECK_EQUAL(parentEntry.GetCountWithDescendants(), testPool.mapTx.size()); BOOST_CHECK_EQUAL(parentEntry.GetSizeWithDescendants(), totalSize); + BOOST_CHECK_EQUAL(parentEntry.GetBillableSizeWithDescendants(), + totalBillableSize); BOOST_CHECK_EQUAL(parentEntry.GetModFeesWithDescendants(), totalFee); } } @@ -436,10 +452,10 @@ */ // take out tx9, tx8 from the beginning sortedOrder.erase(sortedOrder.begin(), sortedOrder.begin() + 2); - sortedOrder.insert(sortedOrder.begin() + 5, tx9.GetId().ToString()); - sortedOrder.insert(sortedOrder.begin() + 6, tx8.GetId().ToString()); - // tx10 is just before tx6 - sortedOrder.insert(sortedOrder.begin() + 7, tx10.GetId().ToString()); + sortedOrder.insert(sortedOrder.begin() + 7, tx9.GetId().ToString()); + sortedOrder.insert(sortedOrder.begin() + 8, tx8.GetId().ToString()); + // tx10 is before tx7 + sortedOrder.insert(sortedOrder.begin() + 9, tx10.GetId().ToString()); CheckSort(pool, sortedOrder, "MempoolIndexingTest6"); // there should be 10 transactions in the mempool @@ -581,7 +597,7 @@ // CTxMemPoolEntry entry7(tx7, fee, 2, 10.0, 1, true); pool.addUnchecked(tx7.GetId(), entry.Fee(Amount(fee)).FromTx(tx7)); BOOST_CHECK_EQUAL(pool.size(), 7UL); - sortedOrder.insert(sortedOrder.begin() + 1, tx7.GetId().ToString()); + sortedOrder.insert(sortedOrder.begin(), tx7.GetId().ToString()); CheckSort(pool, sortedOrder, "MempoolAncestorIndexingTest3"); @@ -590,7 +606,7 @@ vtx.push_back(MakeTransactionRef(tx6)); pool.removeForBlock(vtx, 1); - sortedOrder.erase(sortedOrder.begin() + 1); + sortedOrder.erase(sortedOrder.begin()); // Ties are broken by hash if (tx3.GetId() < tx6.GetId()) { sortedOrder.pop_back(); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -139,7 +139,7 @@ tx.vout[0].nValue = int64_t(5000000000LL - 1000 - 50000) * SATOSHI; TxId freeTxId = tx.GetId(); mempool.addUnchecked(freeTxId, entry.Fee(Amount::zero()).FromTx(tx)); - size_t freeTxSize = ::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION); + size_t freeTxSize = CTransaction(tx).GetBillableSize(); // 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). diff --git a/src/txmempool.h b/src/txmempool.h --- a/src/txmempool.h +++ b/src/txmempool.h @@ -277,14 +277,14 @@ double aModFee = (fUseADescendants ? a.GetModFeesWithDescendants() : a.GetModifiedFee()) / SATOSHI; - double aSize = - fUseADescendants ? a.GetSizeWithDescendants() : a.GetTxSize(); + double aSize = fUseADescendants ? a.GetBillableSizeWithDescendants() + : a.GetTxBillableSize(); double bModFee = (fUseBDescendants ? b.GetModFeesWithDescendants() : b.GetModifiedFee()) / SATOSHI; - double bSize = - fUseBDescendants ? b.GetSizeWithDescendants() : b.GetTxSize(); + double bSize = fUseBDescendants ? b.GetBillableSizeWithDescendants() + : b.GetTxBillableSize(); // Avoid division by rewriting (a/b > c/d) as (a*d > c*b). double f1 = aModFee * bSize; @@ -298,8 +298,10 @@ // Calculate which score to use for an entry (avoiding division). bool UseDescendantScore(const CTxMemPoolEntry &a) const { - double f1 = a.GetSizeWithDescendants() * (a.GetModifiedFee() / SATOSHI); - double f2 = a.GetTxSize() * (a.GetModFeesWithDescendants() / SATOSHI); + double f1 = + a.GetBillableSizeWithDescendants() * (a.GetModifiedFee() / SATOSHI); + double f2 = + a.GetTxBillableSize() * (a.GetModFeesWithDescendants() / SATOSHI); return f2 > f1; } }; @@ -311,8 +313,8 @@ class CompareTxMemPoolEntryByScore { public: bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) const { - double f1 = b.GetTxSize() * (a.GetModifiedFee() / SATOSHI); - double f2 = a.GetTxSize() * (b.GetModifiedFee() / SATOSHI); + double f1 = b.GetTxBillableSize() * (a.GetModifiedFee() / SATOSHI); + double f2 = a.GetTxBillableSize() * (b.GetModifiedFee() / SATOSHI); if (f1 == f2) { return b.GetTx().GetId() < a.GetTx().GetId(); } @@ -331,10 +333,10 @@ public: bool operator()(const CTxMemPoolEntry &a, const CTxMemPoolEntry &b) const { double aFees = a.GetModFeesWithAncestors() / SATOSHI; - double aSize = a.GetSizeWithAncestors(); + double aSize = a.GetBillableSizeWithAncestors(); double bFees = b.GetModFeesWithAncestors() / SATOSHI; - double bSize = b.GetSizeWithAncestors(); + double bSize = b.GetBillableSizeWithAncestors(); // Avoid division by rewriting (a/b > c/d) as (a*d > c*b). double f1 = aFees * bSize; diff --git a/src/txmempool.cpp b/src/txmempool.cpp --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -782,17 +782,20 @@ nNoLimit, nNoLimit, dummy); uint64_t nCountCheck = setAncestors.size() + 1; uint64_t nSizeCheck = it->GetTxSize(); + uint64_t nBillableSizeCheck = it->GetTxBillableSize(); Amount nFeesCheck = it->GetModifiedFee(); int64_t nSigOpCheck = it->GetSigOpCount(); for (txiter ancestorIt : setAncestors) { nSizeCheck += ancestorIt->GetTxSize(); + nBillableSizeCheck += ancestorIt->GetTxBillableSize(); nFeesCheck += ancestorIt->GetModifiedFee(); nSigOpCheck += ancestorIt->GetSigOpCount(); } assert(it->GetCountWithAncestors() == nCountCheck); assert(it->GetSizeWithAncestors() == nSizeCheck); + assert(it->GetBillableSizeWithAncestors() == nBillableSizeCheck); assert(it->GetSigOpCountWithAncestors() == nSigOpCheck); assert(it->GetModFeesWithAncestors() == nFeesCheck); @@ -1246,7 +1249,7 @@ // mempool with feerate equal to txn which were removed with no block in // between. CFeeRate removed(it->GetModFeesWithDescendants(), - it->GetSizeWithDescendants()); + it->GetBillableSizeWithDescendants()); removed += MEMPOOL_FULL_FEE_INCREMENT; trackPackageRemoved(removed); diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -524,6 +524,7 @@ chainActive.Height(), inChainInputValue, fSpendsCoinbase, nSigOpsCount, lp); unsigned int nSize = entry.GetTxSize(); + size_t feeSize = tx.GetBillableSize(); // Check that the transaction doesn't have an excessive number of // sigops, making it impossible to mine. Since the coinbase transaction @@ -541,7 +542,7 @@ pool.GetMinFee( gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000) - .GetFee(nSize); + .GetFee(feeSize); if (mempoolRejectFee > Amount::zero() && nModifiedFees < mempoolRejectFee) { return state.DoS(0, false, REJECT_INSUFFICIENTFEE, @@ -550,7 +551,7 @@ } if (gArgs.GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && - nModifiedFees < minRelayTxFee.GetFee(nSize) && + nModifiedFees < minRelayTxFee.GetFee(feeSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) { // Require that free transactions have sufficient priority to be // mined in the next block. @@ -562,7 +563,7 @@ // This mitigates 'penny-flooding' -- sending thousands of free // transactions just to be annoying or make others' transactions take // longer to confirm. - if (fLimitFree && nModifiedFees < minRelayTxFee.GetFee(nSize)) { + if (fLimitFree && nModifiedFees < minRelayTxFee.GetFee(feeSize)) { static CCriticalSection csFreeLimiter; static double dFreeCount; static int64_t nLastTime; diff --git a/src/wallet/fees.cpp b/src/wallet/fees.cpp --- a/src/wallet/fees.cpp +++ b/src/wallet/fees.cpp @@ -29,8 +29,8 @@ } // Prevent user from paying a fee below minRelayTxFee or minTxFee. - nFeeNeeded = - std::max(nFeeNeeded, GetConfig().GetMinFeePerKB().GetFee(nTxBytes)); + nFeeNeeded = std::max(nFeeNeeded, + GetConfig().GetMinFeePerKB().GetFeeCeiling(nTxBytes)); // But always obey the maximum. if (nFeeNeeded > maxTxFee) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2925,6 +2925,11 @@ CTransaction txNewConst(txNew); unsigned int nBytes = txNewConst.GetTotalSize(); + + // Note: The relaying code has been changed to charge upfront for + // the minimum required bytes to spend a UTXO. This means that + // we need to calculate possible fees based that size. + size_t feeBytes = txNewConst.GetBillableSize(); dPriority = txNewConst.ComputePriority(dPriority, nBytes); // Remove scriptSigs to eliminate the fee calculation dummy @@ -2941,20 +2946,22 @@ } Amount nFeeNeeded = - GetMinimumFee(nBytes, currentConfirmationTarget, mempool); + GetMinimumFee(feeBytes, currentConfirmationTarget, mempool); + if (coinControl && nFeeNeeded > Amount::zero() && coinControl->nMinimumTotalFee > nFeeNeeded) { nFeeNeeded = coinControl->nMinimumTotalFee; } if (coinControl && coinControl->fOverrideFeeRate) { - nFeeNeeded = coinControl->nFeeRate.GetFeeCeiling(nBytes); + nFeeNeeded = coinControl->nFeeRate.GetFeeCeiling(feeBytes); } // If we made it here and we aren't even able to meet the relay fee // on the next pass, give up because we must be at the maximum // allowed fee. - Amount minFee = GetConfig().GetMinFeePerKB().GetFeeCeiling(nBytes); + Amount minFee = + GetConfig().GetMinFeePerKB().GetFeeCeiling(feeBytes); if (nFeeNeeded < minFee) { strFailReason = _("Transaction too large for fee policy"); return false;