Skip to content

Commit 8de5c7d

Browse files
committed
CORE: Add a transaction fee discount for mining/witness inputs
Allow miners/witnesses to consolodate inputs without paying as much as is normally required, we don't have to worry about transaction spam as much with coinbase inputs as they are limited in number by the codebase
1 parent b93972f commit 8de5c7d

File tree

11 files changed

+71
-31
lines changed

11 files changed

+71
-31
lines changed

src/policy/policy.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,3 +321,23 @@ int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost)
321321
{
322322
return GetVirtualTransactionSize(GetTransactionWeight(tx), nSigOpCost);
323323
}
324+
325+
const uint64_t STANDARD_COINBASE_INPUT_SIZE_DISCOUNT = 70;
326+
const uint64_t STANDARD_COINBASE_SIGOP_COST_DISCOUNT = 1;
327+
int64_t GetVirtualTransactionSizeDiscounted(int64_t nWeight, uint64_t numCoinbaseInputs, int64_t nSigOpCost)
328+
{
329+
uint64_t coinbaseInputsToDiscount=(numCoinbaseInputs>2?numCoinbaseInputs-2:numCoinbaseInputs);
330+
uint64_t weightDiscount = STANDARD_COINBASE_INPUT_SIZE_DISCOUNT * coinbaseInputsToDiscount;
331+
uint64_t sigOpCostDiscount = STANDARD_COINBASE_SIGOP_COST_DISCOUNT * coinbaseInputsToDiscount;
332+
uint64_t discountedWeight = nWeight>weightDiscount?nWeight-weightDiscount:nWeight;
333+
uint64_t discountedSigOps = nSigOpCost>sigOpCostDiscount?nSigOpCost-sigOpCostDiscount:nSigOpCost;
334+
335+
return (std::max(discountedWeight, discountedSigOps * nBytesPerSigOp));
336+
}
337+
338+
339+
int64_t GetVirtualTransactionSizeDiscounted(const CTransaction& tx, uint64_t numCoinbaseInputs, int64_t nSigOpCost)
340+
{
341+
uint64_t nTxWeight = GetTransactionWeight(tx);
342+
return GetVirtualTransactionSizeDiscounted(nTxWeight, numCoinbaseInputs, nSigOpCost);
343+
}

src/policy/policy.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,7 @@ extern unsigned int nBytesPerSigOp;
100100
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
101101
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);
102102
int64_t GetVirtualTransactionSize(const CTransaction& tx, int64_t nSigOpCost = 0);
103+
int64_t GetVirtualTransactionSizeDiscounted(int64_t nWeight, uint64_t numCoinbaseInputs, int64_t nSigOpCost);
104+
int64_t GetVirtualTransactionSizeDiscounted(const CTransaction& tx, uint64_t numCoinbaseInputs, int64_t nSigOpCost=0);
103105

104106
#endif

src/primitives/transaction.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1330,7 +1330,6 @@ typedef std::shared_ptr<const CTransaction> CTransactionRef;
13301330
static inline CTransactionRef MakeTransactionRef(int32_t nVersion_) { return std::make_shared<const CTransaction>(nVersion_); }
13311331
template <typename Tx> static inline CTransactionRef MakeTransactionRef(Tx&& txIn) { return std::make_shared<const CTransaction>(std::forward<Tx>(txIn)); }
13321332

1333-
/** Compute the weight of a transaction, as defined by BIP 141 */
13341333
int64_t GetTransactionWeight(const CTransaction &tx);
13351334

13361335
#endif

src/test/test.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -174,5 +174,5 @@ CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) {
174174

175175
CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn) {
176176
return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, nHeight,
177-
spendsCoinbase, sigOpCost, lp);
177+
spendsCoinbase?0:1, sigOpCost, lp);
178178
}

src/txmempool.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@
2929

3030
CTxMemPoolEntry::CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
3131
int64_t _nTime, unsigned int _entryHeight,
32-
bool _spendsCoinbase, int64_t _sigOpsCost, LockPoints lp):
32+
uint64_t _spendCoinbaseCount, int64_t _sigOpsCost, LockPoints lp):
3333
tx(_tx), nFee(_nFee), nTime(_nTime), entryHeight(_entryHeight),
34-
spendsCoinbase(_spendsCoinbase), sigOpCost(_sigOpsCost), lockPoints(lp)
34+
spendCoinbaseCount(_spendCoinbaseCount), sigOpCost(_sigOpsCost), lockPoints(lp)
3535
{
36-
nTxWeight = GetTransactionWeight(*tx);
36+
nTxWeight = tx->GetTotalSize();
3737
nUsageSize = RecursiveDynamicUsage(tx);
3838

3939
nCountWithDescendants = 1;
@@ -70,6 +70,11 @@ size_t CTxMemPoolEntry::GetTxSize() const
7070
return GetVirtualTransactionSize(nTxWeight, sigOpCost);
7171
}
7272

73+
size_t CTxMemPoolEntry::GetTxSizeDiscounted() const
74+
{
75+
return GetVirtualTransactionSizeDiscounted(nTxWeight, spendCoinbaseCount, sigOpCost);
76+
}
77+
7378
// Update the given tx for any in-mempool descendants.
7479
// Assumes that setMemPoolChildren is correct for the given tx and all
7580
// descendants.

src/txmempool.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ class CTxMemPoolEntry
8181
size_t nUsageSize; //!< ... and total memory usage
8282
int64_t nTime; //!< Local time when entering the mempool
8383
unsigned int entryHeight; //!< Chain height when entering the mempool
84-
bool spendsCoinbase; //!< keep track of transactions that spend a coinbase
84+
uint64_t spendCoinbaseCount; //!< keep track of transactions that spend a coinbase, and how many they spend
8585
int64_t sigOpCost; //!< Total sigop cost
8686
int64_t feeDelta; //!< Used for determining the priority of the transaction for mining in a block
8787
LockPoints lockPoints; //!< Track the height and time at which tx was final
@@ -102,7 +102,7 @@ class CTxMemPoolEntry
102102
public:
103103
CTxMemPoolEntry(const CTransactionRef& _tx, const CAmount& _nFee,
104104
int64_t _nTime, unsigned int _entryHeight,
105-
bool spendsCoinbase,
105+
uint64_t spendCoinbaseCount,
106106
int64_t nSigOpsCost, LockPoints lp);
107107

108108
CTxMemPoolEntry(const CTxMemPoolEntry& other);
@@ -111,6 +111,7 @@ class CTxMemPoolEntry
111111
CTransactionRef GetSharedTx() const { return this->tx; }
112112
const CAmount& GetFee() const { return nFee; }
113113
size_t GetTxSize() const;
114+
size_t GetTxSizeDiscounted() const;
114115
size_t GetTxWeight() const { return nTxWeight; }
115116
int64_t GetTime() const { return nTime; }
116117
unsigned int GetHeight() const { return entryHeight; }
@@ -133,7 +134,7 @@ class CTxMemPoolEntry
133134
uint64_t GetSizeWithDescendants() const { return nSizeWithDescendants; }
134135
CAmount GetModFeesWithDescendants() const { return nModFeesWithDescendants; }
135136

136-
bool GetSpendsCoinbase() const { return spendsCoinbase; }
137+
bool GetSpendsCoinbase() const { return spendCoinbaseCount>0; }
137138

138139
uint64_t GetCountWithAncestors() const { return nCountWithAncestors; }
139140
uint64_t GetSizeWithAncestors() const { return nSizeWithAncestors; }

src/validation/validation_mempool.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
242242
if (fPartialChecksOnly)
243243
{
244244
LOCK(pool.cs);
245-
CTxMemPoolEntry entry(ptx, 0, nAcceptTime, chain.Height(), false, 0, LockPoints());
245+
CTxMemPoolEntry entry(ptx, 0, nAcceptTime, chain.Height(), 0, 0, LockPoints());
246246

247247
// Store transaction in memory pool
248248
pool.addUnchecked(hash, entry, false);
@@ -322,18 +322,19 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
322322

323323
// Keep track of transactions that spend a coinbase, which we re-scan
324324
// during reorgs to ensure COINBASE_MATURITY is still met.
325-
bool fSpendsCoinbase = false;
325+
uint64_t spendsCoinbaseCount = 0;
326326
for(const CTxIn &txin : tx.vin) {
327327
const Coin &coin = view.AccessCoin(txin.GetPrevOut());
328328
if (coin.IsCoinBase()) {
329-
fSpendsCoinbase = true;
330-
break;
329+
++spendsCoinbaseCount;
331330
}
332331
}
333332

334333
CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, chainActive.Height(),
335-
fSpendsCoinbase, nSigOpsCost, lp);
334+
spendsCoinbaseCount, nSigOpsCost, lp);
335+
336336
unsigned int nSize = entry.GetTxSize();
337+
unsigned int nDiscountedSize = entry.GetTxSizeDiscounted();
337338

338339
// Check that the transaction doesn't have an excessive number of
339340
// sigops, making it impossible to mine. Since the coinbase transaction
@@ -344,13 +345,13 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
344345
return state.DoS(0, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", false,
345346
strprintf("%d", nSigOpsCost));
346347

347-
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
348+
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nDiscountedSize);
348349
if (mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) {
349350
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", false, strprintf("%d < %d", nFees, mempoolRejectFee));
350351
}
351352

352353
// No transactions are allowed below minRelayTxFee except from disconnected blocks
353-
if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) {
354+
if (fLimitFree && nModifiedFees < ::minRelayTxFee.GetFee(nDiscountedSize)) {
354355
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "min relay fee not met");
355356
}
356357

@@ -401,7 +402,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
401402
const bool fReplacementTransaction = setConflicts.size();
402403
if (fReplacementTransaction)
403404
{
404-
CFeeRate newFeeRate(nModifiedFees, nSize);
405+
CFeeRate newFeeRate(nModifiedFees, nDiscountedSize);
405406
std::set<uint256> setConflictsParents;
406407
const int maxDescendantsToVisit = 100;
407408
CTxMemPool::setEntries setIterConflicting;
@@ -507,14 +508,14 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool
507508
// Finally in addition to paying more fees than the conflicts the
508509
// new transaction must pay for its own bandwidth.
509510
CAmount nDeltaFees = nModifiedFees - nConflictingFees;
510-
if (nDeltaFees < ::incrementalRelayFee.GetFee(nSize))
511+
if (nDeltaFees < ::incrementalRelayFee.GetFee(nDiscountedSize))
511512
{
512513
return state.DoS(0, false,
513514
REJECT_INSUFFICIENTFEE, "insufficient fee", false,
514515
strprintf("rejecting replacement %s, not enough additional fees to relay; %s < %s",
515516
hash.ToString(),
516517
FormatMoney(nDeltaFees),
517-
FormatMoney(::incrementalRelayFee.GetFee(nSize))));
518+
FormatMoney(::incrementalRelayFee.GetFee(nDiscountedSize))));
518519
}
519520
}
520521

src/validation/witnessvalidation.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include "wallet/wallet.h"
1616
#endif
1717

18-
#include <boost/foreach.hpp> // Zreverse_foreach
18+
#include <boost/foreach.hpp> // reverse_foreach
1919
#include <boost/algorithm/string/replace.hpp>
2020
#include <boost/algorithm/string/join.hpp>
2121
#include <boost/thread.hpp>

src/wallet/wallet.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,8 @@ class CInputCoin {
221221
throw std::invalid_argument("walletTx should not be null");
222222
if (i >= walletTx->tx->vout.size())
223223
throw std::out_of_range("The output index is out of range");
224+
225+
isCoinBase = walletTx->tx->IsCoinBase();
224226

225227
if (allowIndexBased && walletTx->GetDepthInMainChain() > COINBASE_MATURITY && walletTx->nHeight > 1 && walletTx->nIndex >= 0)
226228
{
@@ -236,7 +238,8 @@ class CInputCoin {
236238
txout = walletTx->tx->vout[i];
237239
}
238240

239-
CInputCoin(const COutPoint& outpoint_, const CTxOut& txout_, bool allowIndexBased, uint64_t nBlockHeight=0, uint64_t nTxIndex=0)
241+
CInputCoin(const COutPoint& outpoint_, const CTxOut& txout_, bool allowIndexBased, bool isCoinBase_, uint64_t nBlockHeight=0, uint64_t nTxIndex=0)
242+
: isCoinBase(isCoinBase_)
240243
{
241244
if (allowIndexBased && nBlockHeight < (uint64_t)chainActive.Tip()->nHeight && ((uint64_t)chainActive.Tip()->nHeight - nBlockHeight > (uint64_t)COINBASE_MATURITY))
242245
{
@@ -253,6 +256,7 @@ class CInputCoin {
253256

254257
COutPoint outpoint;
255258
CTxOut txout;
259+
bool isCoinBase;
256260

257261
bool operator<(const CInputCoin& rhs) const {
258262
return outpoint < rhs.outpoint;

src/wallet/wallet_transaction.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -550,7 +550,15 @@ bool CWallet::CreateTransaction(std::vector<CKeyStore*>& accountsToTry, const st
550550
}
551551
}
552552

553-
unsigned int nBytes = GetVirtualTransactionSize(txNew);
553+
unsigned int numCoinBase = 0;
554+
for (const auto& coin : setCoins)
555+
{
556+
if (coin.isCoinBase)
557+
{
558+
++numCoinBase;
559+
}
560+
}
561+
unsigned int nBytesDiscounted = GetVirtualTransactionSizeDiscounted(txNew, numCoinBase);
554562

555563
CTransaction txNewConst(txNew);
556564

@@ -565,13 +573,13 @@ bool CWallet::CreateTransaction(std::vector<CKeyStore*>& accountsToTry, const st
565573
if (coinControl && coinControl->nConfirmTarget > 0)
566574
currentConfirmationTarget = coinControl->nConfirmTarget;
567575

568-
CAmount nFeeNeeded = GetMinimumFee(nBytes, currentConfirmationTarget, ::mempool, ::feeEstimator);
576+
CAmount nFeeNeeded = GetMinimumFee(nBytesDiscounted, currentConfirmationTarget, ::mempool, ::feeEstimator);
569577
if (coinControl && coinControl->fOverrideFeeRate)
570-
nFeeNeeded = coinControl->nFeeRate.GetFee(nBytes);
578+
nFeeNeeded = coinControl->nFeeRate.GetFee(nBytesDiscounted);
571579

572580
// If we made it here and we aren't even able to meet the relay fee on the next pass, give up
573581
// because we must be at the maximum allowed fee.
574-
if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))
582+
if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytesDiscounted))
575583
{
576584
strFailReason = _("Transaction too large for fee policy");
577585
return false;
@@ -653,7 +661,7 @@ bool CWallet::CreateTransaction(std::vector<CKeyStore*>& accountsToTry, const st
653661
if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
654662
// Lastly, ensure this tx will pass the mempool's chain limits
655663
LockPoints lp;
656-
CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, false, 0, lp);
664+
CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, lp);
657665
CTxMemPool::setEntries setAncestors;
658666
size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
659667
size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
@@ -927,7 +935,7 @@ bool CWallet::AddFeeForTransaction(CAccount* forAccount, CMutableTransaction& tx
927935
if (GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
928936
// Lastly, ensure this tx will pass the mempool's chain limits
929937
LockPoints lp;
930-
CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, false, 0, lp);
938+
CTxMemPoolEntry entry(wtxNew.tx, 0, 0, 0, 0, 0, lp);
931939
CTxMemPool::setEntries setAncestors;
932940
size_t nLimitAncestors = GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
933941
size_t nLimitAncestorSize = GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
@@ -976,7 +984,7 @@ bool CWallet::PrepareRenewWitnessAccountTransaction(CAccount* funderAccount, CAc
976984
addedAny = true;
977985

978986
// Add witness input
979-
AddTxInput(tx, CInputCoin(witCoin.outpoint, witCoin.coin.out, allowIndexBased, witCoin.coin.nHeight, witCoin.coin.nTxIndex), false);
987+
AddTxInput(tx, CInputCoin(witCoin.outpoint, witCoin.coin.out, allowIndexBased, false, witCoin.coin.nHeight, witCoin.coin.nTxIndex), false);
980988

981989
// Add witness output
982990
CTxOut renewedWitnessTxOutput;
@@ -1070,7 +1078,7 @@ void CWallet::PrepareUpgradeWitnessAccountTransaction(CAccount* funderAccount, C
10701078
if (::IsMine(*targetWitnessAccount, witCoin.coin.out))
10711079
{
10721080
// Add witness input
1073-
AddTxInput(tx, CInputCoin(witCoin.outpoint, witCoin.coin.out, true, witCoin.coin.nHeight, witCoin.coin.nTxIndex), false);
1081+
AddTxInput(tx, CInputCoin(witCoin.outpoint, witCoin.coin.out, true, false,witCoin.coin.nHeight, witCoin.coin.nTxIndex), false);
10741082

10751083
// Add witness output
10761084
CTxOut renewedWitnessTxOutput;

src/wallet/witness_operations.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ void extendwitnessaddresshelper(CAccount* fundingAccount, witnessOutputsInfoVect
124124
CMutableTransaction extendWitnessTransaction(CTransaction::SEGSIG_ACTIVATION_VERSION);
125125
{
126126
// Add the existing witness output as an input
127-
pwallet->AddTxInput(extendWitnessTransaction, CInputCoin(currentWitnessOutpoint, currentWitnessTxOut, true, currentWitnessHeight, currentWitnessTxIndex), false);
127+
pwallet->AddTxInput(extendWitnessTransaction, CInputCoin(currentWitnessOutpoint, currentWitnessTxOut, true, false, currentWitnessHeight, currentWitnessTxIndex), false);
128128

129129
// Add new witness output
130130
CTxOut extendedWitnessTxOutput;
@@ -398,7 +398,7 @@ void rotatewitnessaddresshelper(CAccount* fundingAccount, witnessOutputsInfoVect
398398
for (const auto& [currentWitnessTxOut, currentWitnessHeight, currentWitnessTxIndex, currentWitnessOutpoint]: unspentWitnessOutputs)
399399
{
400400
// Add input
401-
pwallet->AddTxInput(rotateWitnessTransaction, CInputCoin(currentWitnessOutpoint, currentWitnessTxOut, true, currentWitnessHeight, currentWitnessTxIndex), false);
401+
pwallet->AddTxInput(rotateWitnessTransaction, CInputCoin(currentWitnessOutpoint, currentWitnessTxOut, true, false, currentWitnessHeight, currentWitnessTxIndex), false);
402402

403403
// Get witness details
404404
CTxOutPoW2Witness currentWitnessDetails;
@@ -835,7 +835,7 @@ void redistributeandextendwitnessaccount(CWallet* pwallet, CAccount* fundingAcco
835835
// Add all original outputs as inputs
836836
for (const auto& [currentWitnessTxOut, currentWitnessHeight, currentWitnessTxIndex, currentWitnessOutpoint]: unspentWitnessOutputs)
837837
{
838-
pwallet->AddTxInput(witnessTransaction, CInputCoin(currentWitnessOutpoint, currentWitnessTxOut, true, currentWitnessHeight, currentWitnessTxIndex), false);
838+
pwallet->AddTxInput(witnessTransaction, CInputCoin(currentWitnessOutpoint, currentWitnessTxOut, true, false, currentWitnessHeight, currentWitnessTxIndex), false);
839839

840840
CTxOutPoW2Witness details;
841841
if (!GetPow2WitnessOutput(currentWitnessTxOut, details))

0 commit comments

Comments
 (0)