Merge #10817: Redefine Dust and add a discard_rate
f4d00e6
Add a discard_rate (Alex Morcos)b138585
Remove factor of 3 from definition of dust. (Alex Morcos) Pull request description: The definition of dust is redefined to remove the factor of 3. Dust is redefined to be the value of an output such that it would cost that value in fees to (create and) spend the output at the dust relay rate. The previous definition was that it would cost 1/3 of the value. The default dust relay rate is correspondingly increased to 3000 sat/kB so the actual default dust output value of 546 satoshis for a non-segwit output remains unchanged. This commit is a refactor only unless a dustrelayfee is passed on the commandline in which case that number now needs to be increased by a factor of 3 to get the same behavior. -dustrelayfee is a hidden command line option. Note: It's not exactly a refactor due to edge case changes in rounding as evidenced by the required change to the unit test. A discard_rate is added which defaults to 10,000 sat/kB Any change output which would be dust at the discard_rate you are willing to discard completely and add to fee (as well as continuing to pay the fee that would have been needed for creating the change) This would be a nice addition for 0.15 and I think will remain useful for 0.16 with the new coin selection algorithms in discussion, but its not crucial. It does add translation strings, but we could (should?) avoid that by hiding the option Tree-SHA512: 5b6f655354d0ab6b8b6cac1e8d1fe3136d10beb15c6d948fb15bfb105155a9d03684c6240624039b3eed6428b7e60e54216cc8b2f90c4600701e39f646284a9b
This commit is contained in:
commit
9022aa3722
6 changed files with 46 additions and 14 deletions
|
@ -479,7 +479,7 @@ std::string HelpMessage(HelpMessageMode mode)
|
||||||
if (showDebug) {
|
if (showDebug) {
|
||||||
strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", defaultChainParams->RequireStandard()));
|
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("-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("-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("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER));
|
||||||
|
|
|
@ -19,16 +19,18 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
||||||
{
|
{
|
||||||
// "Dust" is defined in terms of dustRelayFee,
|
// "Dust" is defined in terms of dustRelayFee,
|
||||||
// which has units satoshis-per-kilobyte.
|
// 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.
|
// to spend something, then we consider it dust.
|
||||||
// A typical spendable non-segwit txout is 34 bytes big, and will
|
// A typical spendable non-segwit txout is 34 bytes big, and will
|
||||||
// need a CTxIn of at least 148 bytes to spend:
|
// need a CTxIn of at least 148 bytes to spend:
|
||||||
// so dust is a spendable txout less than
|
// 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
|
// A typical spendable segwit txout is 31 bytes big, and will
|
||||||
// need a CTxIn of at least 67 bytes to spend:
|
// need a CTxIn of at least 67 bytes to spend:
|
||||||
// so dust is a spendable txout less than
|
// 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())
|
if (txout.scriptPubKey.IsUnspendable())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -44,7 +46,7 @@ CAmount GetDustThreshold(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
||||||
nSize += (32 + 4 + 1 + 107 + 4); // the 148 mentioned above
|
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)
|
bool IsDust(const CTxOut& txout, const CFeeRate& dustRelayFeeIn)
|
||||||
|
|
|
@ -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;
|
static const unsigned int MAX_STANDARD_P2WSH_STACK_ITEM_SIZE = 80;
|
||||||
/** The maximum size of a standard witnessScript */
|
/** The maximum size of a standard witnessScript */
|
||||||
static const unsigned int MAX_STANDARD_P2WSH_SCRIPT_SIZE = 3600;
|
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
|
* minRelayTxFee, however changing the dust limit changes which transactions are
|
||||||
* standard and should be done with care and ideally rarely. It makes sense to
|
* 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
|
* only increase the dust limit after prior releases were already not creating
|
||||||
* outputs below the new threshold */
|
* 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
|
* Standard script verification flags that standard transactions will comply
|
||||||
* with. However scripts violating these flags may still be present in valid
|
* 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));
|
BOOST_CHECK(IsStandardTx(t, reason));
|
||||||
|
|
||||||
// Check dust with default relay fee:
|
// Check dust with default relay fee:
|
||||||
CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000 * 3;
|
CAmount nDustThreshold = 182 * dustRelayFee.GetFeePerK()/1000;
|
||||||
BOOST_CHECK_EQUAL(nDustThreshold, 546);
|
BOOST_CHECK_EQUAL(nDustThreshold, 546);
|
||||||
// dust:
|
// dust:
|
||||||
t.vout[0].nValue = nDustThreshold - 1;
|
t.vout[0].nValue = nDustThreshold - 1;
|
||||||
|
@ -702,13 +702,13 @@ BOOST_AUTO_TEST_CASE(test_IsStandard)
|
||||||
BOOST_CHECK(IsStandardTx(t, reason));
|
BOOST_CHECK(IsStandardTx(t, reason));
|
||||||
|
|
||||||
// Check dust with odd relay fee to verify rounding:
|
// Check dust with odd relay fee to verify rounding:
|
||||||
// nDustThreshold = 182 * 1234 / 1000 * 3
|
// nDustThreshold = 182 * 3702 / 1000
|
||||||
dustRelayFee = CFeeRate(1234);
|
dustRelayFee = CFeeRate(3702);
|
||||||
// dust:
|
// dust:
|
||||||
t.vout[0].nValue = 672 - 1;
|
t.vout[0].nValue = 673 - 1;
|
||||||
BOOST_CHECK(!IsStandardTx(t, reason));
|
BOOST_CHECK(!IsStandardTx(t, reason));
|
||||||
// not dust:
|
// not dust:
|
||||||
t.vout[0].nValue = 672;
|
t.vout[0].nValue = 673;
|
||||||
BOOST_CHECK(IsStandardTx(t, reason));
|
BOOST_CHECK(IsStandardTx(t, reason));
|
||||||
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
|
dustRelayFee = CFeeRate(DUST_RELAY_TX_FEE);
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,8 @@ CFeeRate CWallet::minTxFee = CFeeRate(DEFAULT_TRANSACTION_MINFEE);
|
||||||
*/
|
*/
|
||||||
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
|
CFeeRate CWallet::fallbackFee = CFeeRate(DEFAULT_FALLBACK_FEE);
|
||||||
|
|
||||||
|
CFeeRate CWallet::m_discard_rate = CFeeRate(DEFAULT_DISCARD_FEE);
|
||||||
|
|
||||||
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
const uint256 CMerkleTx::ABANDON_HASH(uint256S("0000000000000000000000000000000000000000000000000000000000000001"));
|
||||||
|
|
||||||
/** @defgroup mapWallet
|
/** @defgroup mapWallet
|
||||||
|
@ -2524,6 +2526,17 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
|
||||||
return true;
|
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 */);
|
||||||
|
// 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,
|
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)
|
int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
|
||||||
{
|
{
|
||||||
|
@ -2623,6 +2636,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
||||||
CTxOut change_prototype_txout(0, scriptChange);
|
CTxOut change_prototype_txout(0, scriptChange);
|
||||||
size_t change_prototype_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0);
|
size_t change_prototype_size = GetSerializeSize(change_prototype_txout, SER_DISK, 0);
|
||||||
|
|
||||||
|
CFeeRate discard_rate = GetDiscardRate(::feeEstimator);
|
||||||
nFeeRet = 0;
|
nFeeRet = 0;
|
||||||
bool pick_new_inputs = true;
|
bool pick_new_inputs = true;
|
||||||
CAmount nValueIn = 0;
|
CAmount nValueIn = 0;
|
||||||
|
@ -2690,7 +2704,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
||||||
|
|
||||||
// Never create dust outputs; if we would, just
|
// Never create dust outputs; if we would, just
|
||||||
// add the dust to the fee.
|
// add the dust to the fee.
|
||||||
if (IsDust(newTxOut, ::dustRelayFee))
|
if (IsDust(newTxOut, discard_rate))
|
||||||
{
|
{
|
||||||
nChangePosInOut = -1;
|
nChangePosInOut = -1;
|
||||||
nFeeRet += nChange;
|
nFeeRet += nChange;
|
||||||
|
@ -2770,7 +2784,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletT
|
||||||
// (because of reduced tx size) and so we should add a
|
// (because of reduced tx size) and so we should add a
|
||||||
// change output. Only try this once.
|
// change output. Only try this once.
|
||||||
CAmount fee_needed_for_change = GetMinimumFee(change_prototype_size, coin_control, ::mempool, ::feeEstimator, nullptr);
|
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;
|
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) {
|
if (nFeeRet > nFeeNeeded + max_excess_fee && nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
|
||||||
pick_new_inputs = false;
|
pick_new_inputs = false;
|
||||||
|
@ -3811,6 +3825,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("-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)"),
|
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)));
|
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)"),
|
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)));
|
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
|
||||||
strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
|
strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
|
||||||
|
@ -4136,6 +4153,16 @@ bool CWallet::ParameterInteraction()
|
||||||
_("This is the transaction fee you may pay when fee estimates are not available."));
|
_("This is the transaction fee you may pay when fee estimates are not available."));
|
||||||
CWallet::fallbackFee = CFeeRate(nFeePerK);
|
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"));
|
||||||
|
CWallet::m_discard_rate = CFeeRate(nFeePerK);
|
||||||
|
}
|
||||||
if (IsArgSet("-paytxfee"))
|
if (IsArgSet("-paytxfee"))
|
||||||
{
|
{
|
||||||
CAmount nFeePerK = 0;
|
CAmount nFeePerK = 0;
|
||||||
|
|
|
@ -45,6 +45,8 @@ static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000;
|
||||||
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
|
static const CAmount DEFAULT_TRANSACTION_FEE = 0;
|
||||||
//! -fallbackfee default
|
//! -fallbackfee default
|
||||||
static const CAmount DEFAULT_FALLBACK_FEE = 20000;
|
static const CAmount DEFAULT_FALLBACK_FEE = 20000;
|
||||||
|
//! -m_discard_rate default
|
||||||
|
static const CAmount DEFAULT_DISCARD_FEE = 10000;
|
||||||
//! -mintxfee default
|
//! -mintxfee default
|
||||||
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
|
static const CAmount DEFAULT_TRANSACTION_MINFEE = 1000;
|
||||||
//! minimum recommended increment for BIP 125 replacement txs
|
//! minimum recommended increment for BIP 125 replacement txs
|
||||||
|
@ -969,6 +971,7 @@ public:
|
||||||
|
|
||||||
static CFeeRate minTxFee;
|
static CFeeRate minTxFee;
|
||||||
static CFeeRate fallbackFee;
|
static CFeeRate fallbackFee;
|
||||||
|
static CFeeRate m_discard_rate;
|
||||||
/**
|
/**
|
||||||
* Estimate the minimum fee considering user set parameters
|
* Estimate the minimum fee considering user set parameters
|
||||||
* and the required fee
|
* and the required fee
|
||||||
|
|
Loading…
Reference in a new issue