aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Lodder <[email protected]>2021-06-27 00:37:10 +0200
committerPatrick Lodder <[email protected]>2021-08-05 18:24:17 +0200
commit4c46af04830cec3da6ecfbf677ef3d826a5892e6 (patch)
tree8e2c6490458c57ff690788195b298cc4edd07891
parentMerge pull request #2415 from rnicoll/1.14.4-rbf-fee (diff)
downloaddiscoin-4c46af04830cec3da6ecfbf677ef3d826a5892e6.tar.xz
discoin-4c46af04830cec3da6ecfbf677ef3d826a5892e6.zip
[fees] introduce configurable hard dust limit
Co-authored-by: Ross Nicoll <[email protected]>
-rwxr-xr-xqa/pull-tester/rpc-tests.py1
-rw-r--r--qa/rpc-tests/harddustlimit.py82
-rw-r--r--src/init.cpp9
-rw-r--r--src/policy/policy.cpp1
-rw-r--r--src/policy/policy.h8
-rw-r--r--src/primitives/transaction.h4
-rw-r--r--src/wallet/test/wallet_tests.cpp14
7 files changed, 118 insertions, 1 deletions
diff --git a/qa/pull-tester/rpc-tests.py b/qa/pull-tester/rpc-tests.py
index f7a2deb0d..fd78d3701 100755
--- a/qa/pull-tester/rpc-tests.py
+++ b/qa/pull-tester/rpc-tests.py
@@ -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',
diff --git a/qa/rpc-tests/harddustlimit.py b/qa/rpc-tests/harddustlimit.py
new file mode 100644
index 000000000..9d56febb7
--- /dev/null
+++ b/qa/rpc-tests/harddustlimit.py
@@ -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()
diff --git a/src/init.cpp b/src/init.cpp
index 22dc45929..dd1942133 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -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;
diff --git a/src/policy/policy.cpp b/src/policy/policy.cpp
index ec398f662..7b2b23964 100644
--- a/src/policy/policy.cpp
+++ b/src/policy/policy.cpp
@@ -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)
{
diff --git a/src/policy/policy.h b/src/policy/policy.h
index a7fc8b78e..be34b9156 100644
--- a/src/policy/policy.h
+++ b/src/policy/policy.h
@@ -47,6 +47,13 @@ static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
* outputs below the new threshold */
static const unsigned int DUST_RELAY_TX_FEE = 1000;
/**
+ * 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 = 100000000;
+
+/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
* blocks and we must accept those blocks.
@@ -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);
diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h
index 16a92bf2e..0d0ceda54 100644
--- a/src/primitives/transaction.h
+++ b/src/primitives/transaction.h
@@ -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
diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp
index 4e84bb688..79a994bf6 100644
--- a/src/wallet/test/wallet_tests.cpp
+++ b/src/wallet/test/wallet_tests.cpp
@@ -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()