Redefine Dust and add a discard_rate #10817

Merged
merged 2 commits into from Jul 19, 2017
Jump to file or symbol
Failed to load files and symbols.
+46 −14
Split
View
@@ -479,7 +479,7 @@ std::string HelpMessage(HelpMessageMode mode)
if (showDebug) {
strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", defaultChainParams->RequireStandard()));
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("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)));
}
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));
View
@@ -19,16 +19,18 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
{
// "Dust" is defined in terms of dustRelayFee,
// which has units satoshis-per-kilobyte.
- // If you'd pay more than 1/3 in fees
+ // If you'd pay more in fees than the value of the output
// to spend something, then we consider it dust.
// A typical spendable non-segwit txout is 34 bytes big, and will
// need a CTxIn of at least 148 bytes to spend:
// so dust is a spendable txout less than
- // 546*dustRelayFee/1000 (in satoshis).
+ // 182*dustRelayFee/1000 (in satoshis).
+ // 546 satoshis at the default rate of 3000 sat/kB.
// A typical spendable segwit txout is 31 bytes big, and will
// need a CTxIn of at least 67 bytes to spend:
// so dust is a spendable txout less than
- // 294*dustRelayFee/1000 (in satoshis).
+ // 98*dustRelayFee/1000 (in satoshis).
+ // 294 satoshis at the default rate of 3000 sat/kB.
if (txout.scriptPubKey.IsUnspendable())
return 0;
@@ -44,7 +46,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
}
- return 3 * dustRelayFeeIn.GetFee(nSize);
+ return dustRelayFeeIn.GetFee(nSize);
}
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
View
@@ -40,12 +40,12 @@ static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEMS = 100;
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80;
/** The maximum size of a standard witnessScript */
static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
-/** Min feerate for defining dust. Historically this has been the same as the
+/** Min feerate for defining dust. Historically this has been based on the
* minRelayTxFee, however changing the dust limit changes which transactions are
* 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 = 3000;
/**
* Standard script verification flags that standard transactions will comply
* with. However scripts violating these flags may still be present in valid
@@ -692,7 +692,7 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_CHECK(IsStandardTx(t, reason));
// Check dust with default relay fee:
- CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000 * 3;
+ CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000;
BOOST_CHECK_EQUAL(nDustThreshold, 546);
// dust:
t.vout[0].nValue = nDustThreshold - 1;
@@ -702,13 +702,13 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
BOOST_CHECK(IsStandardTx(t, reason));
// Check dust with odd relay fee to verify rounding:
- // nDustThreshold = 182 * 1234 / 1000 * 3
- dustRelayFee = CFeeRate(1234);
+ // nDustThreshold = 182 * 3702 / 1000
+ dustRelayFee = CFeeRate(3702);
// dust:
- t.vout[0].nValue = 672 - 1;
+ t.vout[0].nValue = 673 - 1;
BOOST_CHECK(!IsStandardTx(t, reason));
// not dust:
- t.vout[0].nValue = 672;
+ t.vout[0].nValue = 673;
BOOST_CHECK(IsStandardTx(t, reason));
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
View
@@ -57,6 +57,8 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
*/
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
+CFeeRate CWallet::m_discard_rate = CFeeRate(DEFAULT_DISCARD_FEE);
+
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
/** @defgroup mapWallet
@@ -2501,6 +2503,17 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
return true;
}
+static CFeeRate GetDiscardRate(const CBlockPolicyEstimator& estimator)
+{
+ unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
+ CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */);
@TheBlueMatt

TheBlueMatt Jul 17, 2017

Contributor

Dont you need to check that this wasnt an error return (ie 0)?

@instagibbs

instagibbs Jul 17, 2017

Member

i.e., failure to get a real estimate means we'll go to dustRelayFee instead of static discard rate

@morcos

morcos Jul 17, 2017

Contributor

Yes I suppose that would be a slight improvement. Stupid 0 as error value

+ // Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate
+ discard_rate = (discard_rate == CFeeRate(0)) ? CWallet::m_discard_rate : std::min(discard_rate, CWallet::m_discard_rate);
+ // Discard rate must be at least dustRelayFee
+ discard_rate = std::max(discard_rate, ::dustRelayFee);
+ return discard_rate;
+}
+
bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
{
@@ -2600,6 +2613,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
CTxOut change_prototype_txout(0, scriptChange);
size_t change_prototype_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0);
+ CFeeRate discard_rate = GetDiscardRate(::feeEstimator);
nFeeRet = 0;
bool pick_new_inputs = true;
CAmount nValueIn = 0;
@@ -2667,7 +2681,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// Never create dust outputs; if we would, just
// add the dust to the fee.
- if (IsDust(newTxOut, ::dustRelayFee))
+ if (IsDust(newTxOut, discard_rate))
{
nChangePosInOut = -1;
nFeeRet += nChange;
@@ -2747,7 +2761,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
// (because of reduced tx size) and so we should add a
// change output. Only try this once.
CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr);
- CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, ::dustRelayFee);
+ CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
CAmount max_excess_fee = fee_needed_for_change + minimum_value_for_change;
if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
pick_new_inputs = false;
@@ -3788,6 +3802,9 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE));
strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)));
+ strUsage += HelpMessageOpt("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) used to discard change (to fee) if it would be dust at this fee rate (default: %s) "
+ "Note: We will always discard up to the dust relay fee and a discard fee above that is limited by the longest target fee estimate"),
+ CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)));
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
@@ -4113,6 +4130,16 @@ bool CWallet::ParameterInteraction()
_("This is the transaction fee you may pay when fee estimates are not available."));
CWallet::fallbackFee = CFeeRate(nFeePerK);
}
+ if (IsArgSet("-discardfee"))
+ {
+ CAmount nFeePerK = 0;
+ if (!ParseMoney(GetArg("-discardfee", ""), nFeePerK))
+ return InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), GetArg("-discardfee", "")));
+ if (nFeePerK > HIGH_TX_FEE_PER_KB)
+ InitWarning(AmountHighWarn("-discardfee") + " " +
+ _("This is the transaction fee you may discard if change is smaller than dust at this level"));
@ryanofsky

ryanofsky Jul 17, 2017

Contributor

In commit "Add a discard_rate"

Pedantic, but maybe s/transaction fee/maximum transaction fee/

+ CWallet::m_discard_rate = CFeeRate(nFeePerK);
+ }
if (IsArgSet("-paytxfee"))
{
CAmount nFeePerK = 0;
View
@@ -45,6 +45,8 @@ static const unsigned int DEFAULT_KEYPOOL_SIZE = 100;
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
//! -fallbackfee default
static const CAmount DEFAULT_FALLBACK_FEE = 20000;
+//! -m_discard_rate default
+static const CAmount DEFAULT_DISCARD_FEE = 10000;
//! -mintxfee default
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
//! minimum recommended increment for BIP 125 replacement txs
@@ -965,6 +967,7 @@ class CWallet : public CCryptoKeyStore, public CValidationInterface
static CFeeRate minTxFee;
static CFeeRate fallbackFee;
+ static CFeeRate m_discard_rate;
/**
* Estimate the minimum fee considering user set parameters
* and the required fee