Initial back end framework to separate wallet and relay fees + dust.

This commit is contained in:
Michi Lumin 2021-05-06 15:30:55 -06:00 committed by michilumin
parent 6bea578da0
commit 575f734eec
17 changed files with 137 additions and 70 deletions

View File

@ -516,7 +516,7 @@ class RawTransactionsTest(BitcoinTestFramework):
#fund a tx with ~20 small inputs
inputs = []
# Dogecoin: TX size rounding gives us a fee of 4 DOGE
# Dogecoin: TX size rounding gives us a fee of 1 DOGE. 20 - 15 - 1 = 4 DOGE change
outputs = {self.nodes[0].getnewaddress():15,self.nodes[0].getnewaddress():4}
rawTx = self.nodes[1].createrawtransaction(inputs, outputs)
fundedTx = self.nodes[1].fundrawtransaction(rawTx)

View File

@ -47,7 +47,7 @@ MY_RELAY = 1 # from version 70001 onwards, fRelay should be appended to version
MAX_INV_SZ = 50000
MAX_BLOCK_BASE_SIZE = 1000000
COIN = 100000000 # 1 btc in satoshis
COIN = 100000000 # mlumin 5/2021: In terms of Dogecoin, 1 dogecoin or 100,000,000 koinu.
NODE_NETWORK = (1 << 0)
NODE_GETUTXO = (1 << 1)

View File

@ -102,6 +102,8 @@ BITCOIN_CORE_H = \
cuckoocache.h \
dogecoin.cpp \
dogecoin.h \
dogecoin-fees.cpp \
dogecoin-fees.h \
httprpc.h \
httpserver.h \
indirectmap.h \

76
src/dogecoin-fees.cpp Normal file
View File

@ -0,0 +1,76 @@
// Copyright (c) 2021 The Dogecoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/random/uniform_int.hpp>
#include "policy/policy.h"
#include "arith_uint256.h"
#include "dogecoin.h"
#include "txmempool.h"
#include "util.h"
#include "validation.h"
#include "dogecoin-fees.h"
#include "amount.h"
#ifdef ENABLE_WALLET
#include "wallet/wallet.h"
#endif
#ifdef ENABLE_WALLET
//mlumin 5/2021: walletfees, all attached to GetDogecoinWalletFeeRate which is just the newly exposed ::minWalletTxFee
CAmount GetDogecoinWalletFee(size_t nBytes_)
{
//mlumin: super simple fee calc for dogecoin
CAmount nFee=GetDogecoinWalletFeeRate().GetFee(nBytes_);
}
//mlumin 5/2021: Establish a wallet rate of n koinu per kb.
//mlumin: this is somewhat redundant to the globally exposed ::minWalletTxFee, but honestly I'd like to have both the rate and amount (with size) here
CFeeRate GetDogecoinWalletFeeRate()
{
//mlumin 5/2021: currently 1x COIN or 1 dogecoin or 100,000,000 koinu
return ::minWalletTxFeeRate;
}
#endif
CAmount GetDogecoinMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree)
{
{
LOCK(mempool.cs);
uint256 hash = tx.GetHash();
double dPriorityDelta = 0;
CAmount nFeeDelta = 0;
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
if (dPriorityDelta > 0 || nFeeDelta > 0)
return 0;
}
CAmount nMinFee = ::minRelayTxFeeRate.GetFee(nBytes);
nMinFee += GetDogecoinDustFee(tx.vout, ::minRelayTxFeeRate);
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;
}
CAmount GetDogecoinDustFee(const std::vector<CTxOut> &vout, CFeeRate &baseFeeRate) {
CAmount nFee = 0;
// To limit dust spam, add base fee for each output less than a COIN
BOOST_FOREACH(const CTxOut& txout, vout)
if (txout.IsDust(::minRelayTxFeeRate))
nFee += baseFeeRate.GetFeePerK();
return nFee;
}

14
src/dogecoin-fees.h Normal file
View File

@ -0,0 +1,14 @@
// Copyright (c) 2021 The Dogecoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "amount.h"
#include "chain.h"
#include "chainparams.h"
#ifdef ENABLE_WALLET
CFeeRate GetDogecoinWalletFeeRate();
CAmount GetDogecoinMinWalletFee(unsigned int nBytes_);
#endif
CAmount GetDogecoinMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
CAmount GetDogecoinDustFee(const std::vector<CTxOut> &vout, CFeeRate &baseFeeRate);

View File

@ -11,6 +11,7 @@
#include "txmempool.h"
#include "util.h"
#include "validation.h"
#include "dogecoin-fees.h"
int static generateMTRandom(unsigned int s, int range)
{
@ -146,43 +147,4 @@ CAmount GetDogecoinBlockSubsidy(int nHeight, const Consensus::Params& consensusP
}
}
CAmount GetDogecoinMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree)
{
{
LOCK(mempool.cs);
uint256 hash = tx.GetHash();
double dPriorityDelta = 0;
CAmount nFeeDelta = 0;
mempool.ApplyDeltas(hash, dPriorityDelta, nFeeDelta);
if (dPriorityDelta > 0 || nFeeDelta > 0)
return 0;
}
CAmount nMinFee = ::minRelayTxFee.GetFee(nBytes);
nMinFee += GetDogecoinDustFee(tx.vout, ::minRelayTxFee);
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;
}
CAmount GetDogecoinDustFee(const std::vector<CTxOut> &vout, CFeeRate &baseFeeRate) {
CAmount nFee = 0;
// To limit dust spam, add base fee for each output less than a COIN
BOOST_FOREACH(const CTxOut& txout, vout)
if (txout.IsDust(::minRelayTxFee))
nFee += baseFeeRate.GetFeePerK();
return nFee;
}

View File

@ -18,5 +18,4 @@ unsigned int CalculateDogecoinNextWorkRequired(const CBlockIndex* pindexLast, in
*/
bool CheckAuxPowProofOfWork(const CBlockHeader& block, const Consensus::Params& params);
CAmount GetDogecoinMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree);
CAmount GetDogecoinDustFee(const std::vector<CTxOut> &vout, CFeeRate &baseFeeRate);

View File

@ -1012,11 +1012,11 @@ bool AppInitParameterInteraction()
if (!ParseMoney(GetArg("-minrelaytxfee", ""), n) || 0 == n)
return InitError(AmountErrMsg("minrelaytxfee", GetArg("-minrelaytxfee", "")));
// High fee check is done afterward in CWallet::ParameterInteraction()
::minRelayTxFee = CFeeRate(n);
} else if (incrementalRelayFee > ::minRelayTxFee) {
::minRelayTxFeeRate = CFeeRate(n);
} else if (incrementalRelayFee > ::minRelayTxFeeRate) {
// Allow only setting incrementalRelayFee to control both
::minRelayTxFee = incrementalRelayFee;
LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFee.ToString());
::minRelayTxFeeRate = incrementalRelayFee;
LogPrintf("Increasing minrelaytxfee to %s to match incrementalrelayfee\n",::minRelayTxFeeRate.ToString());
}
// Sanity check argument for min fee for including tx in block

View File

@ -3317,9 +3317,9 @@ bool SendMessages(CNode* pto, CConnman& connman, const std::atomic<bool>& interr
static CFeeRate default_feerate(DEFAULT_MIN_RELAY_TX_FEE);
static FeeFilterRounder filterRounder(default_feerate);
CAmount filterToSend = filterRounder.round(currentFilter);
// If we don't allow free transactions, then we always have a fee filter of at least minRelayTxFee
// If we don't allow free transactions, then we always have a fee filter of at least minRelayTxFeeRate
if (GetArg("-limitfreerelay", DEFAULT_LIMITFREERELAY) <= 0)
filterToSend = std::max(filterToSend, ::minRelayTxFee.GetFeePerK());
filterToSend = std::max(filterToSend, ::minRelayTxFeeRate.GetFeePerK());
if (filterToSend != pto->lastSentFeeFilter) {
connman.PushMessage(pto, msgMaker.Make(NetMsgType::FEEFILTER, filterToSend));
pto->lastSentFeeFilter = filterToSend;

View File

@ -161,7 +161,7 @@ public:
return (nValue == -1);
}
CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const
CAmount GetDustThreshold(const CFeeRate &minRelayTxFeeRate) const
{
// "Dust" is defined in terms of CTransaction::minRelayTxFee,
// which has units satoshis-per-kilobyte.
@ -198,9 +198,9 @@ public:
return COIN;
}
bool IsDust(const CFeeRate &minRelayTxFee) const
bool IsDust(const CFeeRate &minRelayTxFeeRate) const
{
return (nValue < GetDustThreshold(minRelayTxFee));
return (nValue < GetDustThreshold(minRelayTxFeeRate));
}
friend bool operator==(const CTxOut& a, const CTxOut& b)

View File

@ -18,7 +18,7 @@
#include "base58.h"
#include "chainparams.h"
#include "wallet/coincontrol.h"
#include "validation.h" // mempool and minRelayTxFee
#include "validation.h" // mempool and minRelayTxFeeRate
#include "ui_interface.h"
#include "txmempool.h"
#include "wallet/wallet.h"
@ -591,6 +591,7 @@ void SendCoinsDialog::updateFeeSectionControls()
ui->labelFeeEstimation ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeNormal ->setEnabled(ui->radioSmartFee->isChecked());
ui->labelSmartFeeFast ->setEnabled(ui->radioSmartFee->isChecked());
//mlumin: 5/2021 - this label actually gates the 'slider and smart fee' functionality, so turn it off for dogecoin.
ui->confirmationTargetLabel ->setEnabled(ui->radioSmartFee->isChecked());
ui->checkBoxMinimumFee ->setEnabled(ui->radioCustomFee->isChecked());
ui->labelMinFeeWarning ->setEnabled(ui->radioCustomFee->isChecked());

View File

@ -103,7 +103,7 @@ UniValue getinfo(const JSONRPCRequest& request)
obj.push_back(Pair("unlocked_until", nWalletUnlockTime));
obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK())));
#endif
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFeeRate.GetFeePerK())));
obj.push_back(Pair("errors", GetWarnings("statusbar")));
return obj;
}

View File

@ -448,7 +448,7 @@ UniValue getnetworkinfo(const JSONRPCRequest& request)
obj.push_back(Pair("connections", (int)g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)));
}
obj.push_back(Pair("networks", GetNetworksInfo()));
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())));
obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFeeRate.GetFeePerK())));
obj.push_back(Pair("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK())));
UniValue localAddresses(UniValue::VARR);
{

View File

@ -14,6 +14,7 @@
#include "consensus/merkle.h"
#include "consensus/validation.h"
#include "dogecoin.h"
#include "dogecoin-fees.h"
#include "hash.h"
#include "init.h"
#include "policy/fees.h"
@ -83,10 +84,11 @@ bool fEnableReplacement = DEFAULT_ENABLE_REPLACEMENT;
uint256 hashAssumeValid;
CFeeRate minRelayTxFee = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
//mlumin 5/2021: Changing this variable to a fee rate, because that's what it is, not a fee. Confusion bad.
CFeeRate minRelayTxFeeRate = CFeeRate(DEFAULT_MIN_RELAY_TX_FEE);
CAmount maxTxFee = DEFAULT_TRANSACTION_MAXFEE;
CTxMemPool mempool(::minRelayTxFee);
CTxMemPool mempool(::minRelayTxFeeRate);
/**
* Returns true if there are nRequired or more blocks of minVersion or above
@ -767,7 +769,7 @@ bool AcceptToMemoryPoolWorker(CTxMemPool& pool, CValidationState& state, const C
CAmount mempoolRejectFee = pool.GetMinFee(GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize);
if (mempoolRejectFee > 0 && nModifiedFees < 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) && nModifiedFees < ::minRelayTxFee.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) {
} else if (GetBoolArg("-relaypriority", DEFAULT_RELAYPRIORITY) && nModifiedFees < ::minRelayTxFeeRate.GetFee(nSize) && !AllowFree(entry.GetPriority(chainActive.Height() + 1))) {
// Require that free transactions have sufficient priority to be mined in the next block.
return state.DoS(0, false, REJECT_INSUFFICIENTFEE, "insufficient priority");
}

View File

@ -58,9 +58,11 @@ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = COIN;
//! -maxtxfee default
static const CAmount DEFAULT_TRANSACTION_MAXFEE = 400 * COIN;
//! Discourage users to set fees higher than this amount (in satoshis) per kB
static const CAmount HIGH_TX_FEE_PER_KB = 25 * COIN;
//mlumin: 5/2021 adjusted downward for fee revisions
static const CAmount HIGH_TX_FEE_PER_KB = 10 * COIN;
//! -maxtxfee will warn if called with a higher fee than this amount (in satoshis)
static const CAmount HIGH_MAX_TX_FEE = 10 * HIGH_TX_FEE_PER_KB;
//mlumin: 5/2021 adjusted max upward in terms of coin
static const CAmount HIGH_MAX_TX_FEE = 100 * HIGH_TX_FEE_PER_KB;
/** Default for -limitancestorcount, max number of in-mempool ancestors */
static const unsigned int DEFAULT_ANCESTOR_LIMIT = 25;
/** Default for -limitancestorsize, maximum kilobytes of tx + all in-mempool ancestors */
@ -175,7 +177,8 @@ extern bool fCheckBlockIndex;
extern bool fCheckpointsEnabled;
extern size_t nCoinCacheUsage;
/** A fee rate smaller than this is considered zero fee (for relaying, mining and transaction creation) */
extern CFeeRate minRelayTxFee;
//mlumin 5/2021: changing variable name to Rate vs Fee because thats what it is.
extern CFeeRate minRelayTxFeeRate;
/** Absolute maximum transaction fee (in satoshis) used by wallet and mempool (rejects high fee in sendrawtransaction) */
extern CAmount maxTxFee;
extern bool fAlerts;

View File

@ -9,6 +9,7 @@
#include "checkpoints.h"
#include "chain.h"
#include "dogecoin.h"
#include "dogecoin-fees.h"
#include "wallet/coincontrol.h"
#include "consensus/consensus.h"
#include "consensus/validation.h"
@ -38,6 +39,8 @@ using namespace std;
CWallet* pwalletMain = NULL;
/** Transaction fee set by the user */
CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE);
//mlumin 5/2021: Add minimum wallet tx fee. This really should be expressed as a rate not a final fee.
CFeeRate minWalletTxFeeRate = CFeeRate(DEFAULT_MIN_WALLET_TX_FEE);
unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET;
bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
@ -2680,7 +2683,7 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
// 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.
if (nFeeNeeded < ::minRelayTxFee.GetFee(nBytes))
if (nFeeNeeded < ::minRelayTxFeeRate.GetFee(nBytes))
{
strFailReason = _("Transaction too large for fee policy");
return false;
@ -2843,12 +2846,12 @@ bool CWallet::AddAccountingEntry(const CAccountingEntry& acentry, CWalletDB *pwa
CAmount CWallet::GetRequiredFee(const CMutableTransaction& tx, unsigned int nTxBytes)
{
// Dogecoin: Add an increased fee for each dust output
return std::max(minTxFee.GetFee(nTxBytes) + GetDogecoinDustFee(tx.vout, minTxFee), ::minRelayTxFee.GetFee(nTxBytes));
return std::max(minTxFee.GetFee(nTxBytes) + GetDogecoinDustFee(tx.vout, minTxFee), ::minRelayTxFeeRate.GetFee(nTxBytes));
}
CAmount CWallet::GetRequiredFee(unsigned int nTxBytes)
{
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFee.GetFee(nTxBytes));
return std::max(minTxFee.GetFee(nTxBytes), ::minRelayTxFeeRate.GetFee(nTxBytes));
}
CAmount CWallet::GetMinimumFee(const CMutableTransaction& tx, unsigned int nTxBytes, unsigned int nConfirmTarget, const CTxMemPool& pool)
@ -3852,7 +3855,7 @@ bool CWallet::ParameterInteraction()
if (GetArg("-prune", 0) && GetBoolArg("-rescan", false))
return InitError(_("Rescans are not possible in pruned mode. You will need to use -reindex which will download the whole blockchain again."));
if (::minRelayTxFee.GetFeePerK() > HIGH_TX_FEE_PER_KB)
if (::minRelayTxFeeRate.GetFeePerK() > HIGH_TX_FEE_PER_KB)
InitWarning(AmountHighWarn("-minrelaytxfee") + " " +
_("The wallet will avoid paying less than the minimum relay fee."));
@ -3886,10 +3889,10 @@ bool CWallet::ParameterInteraction()
_("This is the transaction fee you will pay if you send a transaction."));
payTxFee = CFeeRate(nFeePerK, 1000);
if (payTxFee < ::minRelayTxFee)
if (payTxFee < ::minRelayTxFeeRate)
{
return InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
GetArg("-paytxfee", ""), ::minRelayTxFeeRate.ToString()));
}
}
if (IsArgSet("-maxtxfee"))
@ -3900,10 +3903,10 @@ bool CWallet::ParameterInteraction()
if (nMaxFee > HIGH_MAX_TX_FEE)
InitWarning(_("-maxtxfee is set very high! Fees this large could be paid on a single transaction."));
maxTxFee = nMaxFee;
if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFee)
if (CFeeRate(maxTxFee, 1000) < ::minRelayTxFeeRate)
{
return InitError(strprintf(_("Invalid amount for -maxtxfee=<amount>: '%s' (must be at least the minrelay fee of %s to prevent stuck transactions)"),
GetArg("-maxtxfee", ""), ::minRelayTxFee.ToString()));
GetArg("-maxtxfee", ""), ::minRelayTxFeeRate.ToString()));
}
}
nTxConfirmTarget = GetArg("-txconfirmtarget", DEFAULT_TX_CONFIRM_TARGET);

View File

@ -38,6 +38,8 @@ extern CWallet* pwalletMain;
* Settings
*/
extern CFeeRate payTxFee;
//mlumin 5/2021: add minWalletTxFee to separate out wallet and relay.
extern CFeeRate minWalletTxFeeRate;
extern unsigned int nTxConfirmTarget;
extern bool bSpendZeroConfChange;
extern bool fSendFreeTransactions;
@ -45,11 +47,14 @@ extern bool fWalletRbf;
static const unsigned int DEFAULT_KEYPOOL_SIZE = 100;
//! -paytxfee default
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
static const CAmount DEFAULT_TRANSACTION_FEE = COIN;
//! -fallbackfee default
//mlumin: 5/2021 scaled minimum, this likely will have to change for fee reduction
static const CAmount DEFAULT_FALLBACK_FEE = COIN;
//! -mintxfee default
static const CAmount DEFAULT_TRANSACTION_MINFEE = COIN;
//mlumin 5/2021: adding a minimum Wallet fee vs relay, currently still 1 COIN, to be reduced.
static const unsigned int DEFAULT_MIN_WALLET_TX_FEE = COIN;
//! minimum recommended increment for BIP 125 replacement txs
static const CAmount WALLET_INCREMENTAL_RELAY_FEE = COIN * 5;
//! target minimum change amount