Merge pull request #2348 from patricklodder/1.14-cfg-dust-limit

[fees] introduce configurable hard dust limit
This commit is contained in:
Ross Nicoll 2021-08-05 22:21:53 +01:00 committed by GitHub
commit 5db95e5a5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 119 additions and 2 deletions

View file

@ -153,6 +153,7 @@ testScripts = [
'signmessages.py',
# 'nulldummy.py',
'import-rescan.py',
'harddustlimit.py',
# While fee bumping should work in Doge, these tests depend on free transactions, which we don't support.
# Disable until we can do a full rewrite of the tests (possibly upstream), or revise fee schedule, or something
'bumpfee.py',

View file

@ -0,0 +1,82 @@
#!/usr/bin/env python3
# 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.
"""Hard dust limit QA test.
# Tests nodes with differing -dustlimits
"""
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import *
from decimal import Decimal
class HardDustLimitTest(BitcoinTestFramework):
def __init__(self):
super().__init__()
self.setup_clean_chain = True
self.num_nodes = 3
def setup_network(self, split=False):
self.nodes = []
self.nodes.append(start_node(0, self.options.tmpdir, ["-dustlimit=0.1", "-debug"]))
self.nodes.append(start_node(1, self.options.tmpdir, ["-dustlimit=1", "-debug"]))
self.nodes.append(start_node(2, self.options.tmpdir, ["-dustlimit=0.01", "-debug"]))
connect_nodes_bi(self.nodes,0,1)
connect_nodes_bi(self.nodes,1,2)
connect_nodes_bi(self.nodes,0,2)
self.is_network_split=False
self.sync_all()
def run_test(self):
self.fee = Decimal("1")
self.seed = 1000
self.coinselector = {'minimumAmount': self.fee * 10, 'maximumAmount': self.seed}
# set up addresses
n0a1 = self.nodes[0].getnewaddress()
n0a2 = self.nodes[0].getnewaddress()
n0a3 = self.nodes[0].getnewaddress()
n1a1 = self.nodes[1].getnewaddress()
n2a1 = self.nodes[2].getnewaddress()
n2a2 = self.nodes[2].getnewaddress()
n2a3 = self.nodes[2].getnewaddress()
n2a4 = self.nodes[2].getnewaddress()
# mine some blocks and prepare some coins
self.nodes[2].generate(1)
self.sync_all()
self.nodes[0].generate(101)
self.sync_all()
self.nodes[0].sendtoaddress(n0a1, self.seed)
self.nodes[0].sendtoaddress(n2a1, self.seed)
self.sync_all()
self.nodes[0].generate(1)
self.sync_all()
# create dusty transactions
self.send_dusty_tx(self.nodes[2], n2a2, n0a2, Decimal("0.9"))
self.send_dusty_tx(self.nodes[2], n2a3, n0a3, Decimal("0.09"))
self.send_dusty_tx(self.nodes[0], n2a4, n1a1, Decimal("1"))
# wait 10 seconds to sync mempools
time.sleep(10)
assert_equal(self.nodes[2].getmempoolinfo()['size'], 3)
assert_equal(self.nodes[1].getmempoolinfo()['size'], 1)
assert_equal(self.nodes[0].getmempoolinfo()['size'], 2)
def send_dusty_tx(self, n, addr1, addr2, dust):
avail = n.listunspent(0, 1000, [], True, self.coinselector)
inputs = [ {'txid': avail[0]['txid'], 'vout': avail[0]['vout']}]
outputs = { addr1 : avail[0]['amount'] - self.fee - dust , addr2: dust }
rawtx = n.createrawtransaction(inputs, outputs)
rawtx = n.signrawtransaction(rawtx)
n.sendrawtransaction(rawtx['hex'])
if __name__ == '__main__':
HardDustLimitTest().main()

View file

@ -475,6 +475,7 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += HelpMessageOpt("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)));
strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost about 1/3 of its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)));
}
strUsage += HelpMessageOpt("-dustlimit=<amt>", strprintf(_("Amount under which a transaction output is considered dust, in %s (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_DUST_LIMIT)));
strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP));
strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER));
strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY));
@ -1043,6 +1044,14 @@ bool AppInitParameterInteraction()
return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString()));
nBytesPerSigOp = GetArg("-bytespersigop", nBytesPerSigOp);
if (IsArgSet("-dustlimit"))
{
CAmount n = nDustLimit;
if (!ParseMoney(GetArg("-dustlimit", ""), n))
return InitError(AmountErrMsg("dustlimit", GetArg("-dustlimit", "")));
nDustLimit = n;
}
#ifdef ENABLE_WALLET
if (!CWallet::ParameterInteraction())
return false;

View file

@ -209,6 +209,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
CFeeRate incrementalRelayFee = CFeeRate(DEFAULT_INCREMENTAL_RELAY_FEE);
CFeeRate dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
unsigned int nBytesPerSigOp = DEFAULT_BYTES_PER_SIGOP;
unsigned int nDustLimit = DEFAULT_DUST_LIMIT;
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost)
{

View file

@ -45,7 +45,14 @@ static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
* standard and should be done with care and ideally rarely. It makes sense to
* only increase the dust limit after prior releases were already not creating
* outputs below the new threshold */
static const unsigned int DUST_RELAY_TX_FEE = 1000;
static const unsigned int DUST_RELAY_TX_FEE = COIN / 100000;
/**
* Dogecoin: Default dust limit that is evaluated when considering whether a
* transaction output is required to pay additional fee for relay and inclusion
* in blocks. Overridden by -dustlimit
*/
static const unsigned int DEFAULT_DUST_LIMIT = COIN / 1;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
@ -96,6 +103,7 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
extern CFeeRate incrementalRelayFee;
extern CFeeRate dustRelayFee;
extern unsigned int nBytesPerSigOp;
extern unsigned int nDustLimit;
/** Compute the virtual transaction size (weight reinterpreted as bytes). */
int64_t GetVirtualTransactionSize(int64_t nWeight, int64_t nSigOpCost);

View file

@ -15,6 +15,8 @@ static const int SERIALIZE_TRANSACTION_NO_WITNESS = 0x40000000;
static const int WITNESS_SCALE_FACTOR = 4;
extern unsigned int nDustLimit;
/** An outpoint - a combination of a transaction hash and an index n into its vout */
class COutPoint
{
@ -195,7 +197,7 @@ public:
*/
// Dogecoin: Anything below 1 DOGE is always dust
return COIN;
return nDustLimit;
}
bool IsDust(const CFeeRate &minRelayTxFeeRate) const

View file

@ -36,6 +36,8 @@ std::vector<std::unique_ptr<CWalletTx>> wtxn;
typedef set<pair<const CWalletTx*,unsigned int> > CoinSet;
extern unsigned int nDustLimit;
BOOST_FIXTURE_TEST_SUITE(wallet_tests, WalletTestingSetup)
static const CWallet wallet;
@ -524,6 +526,18 @@ BOOST_AUTO_TEST_CASE(GetMinimumFee_dust_test)
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 963, 0, pool), 2 * nMinTxFee);
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 1000, 0, pool), 2 * nMinTxFee);
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 1999, 0, pool), 3 * nMinTxFee);
// change the hard dust limit
nDustLimit = COIN / 10;
// Confirm dust penalty fees are not added
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 963, 0, pool), 1 * nMinTxFee);
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 1000, 0, pool), 1 * nMinTxFee);
BOOST_CHECK_EQUAL(CWallet::GetMinimumFee(tx, 1999, 0, pool), 2 * nMinTxFee);
nDustLimit = COIN;
}
BOOST_AUTO_TEST_SUITE_END()