wallet: introduce fee_rate sat/vB param/option #20305

pull jonatack wants to merge 13 commits into bitcoin:master from jonatack:fee_rate_sat_vb changing 15 files +447 −426
  1. jonatack commented at 6:26 am on November 5, 2020: member

    This PR builds on #11413 and #20220 to address #19543.

    • replace overloading the conf_target and estimate_mode params with fee_rate in sat/vB in the sendtoaddress, sendmany, send, fundrawtransaction, walletcreatefundedpsbt, and bumpfee RPCs

    • allow non-actionable conf_target value of 0 and estimate_mode value of "" to be passed to use fee_rate as a positional argument, in addition to as a named argument

    • fix a bug in the experimental send RPC described in #20220 (review) where args were not being passed correctly into the options values

    • update the feerate error message units for these RPCs from BTC/kB to sat/vB

    • update the test coverage, help docs, doxygen docs, and some of the RPC examples

    • other changes to address the excellent review feedback

    See this wallet meeting log for more context: http://www.erisian.com.au/bitcoin-core-dev/log-2020-11-06.html#l-309

  2. fanquake added the label Wallet on Nov 5, 2020
  3. MarcoFalke commented at 7:18 am on November 5, 2020: member
    How does this differ/compare to #20250 ?
  4. MarcoFalke commented at 7:25 am on November 5, 2020: member
    I guess this one is adding a new separate option, #20250 is adding named aliases and keeping the existing options.
  5. jonatack commented at 7:42 am on November 5, 2020: member

    Yes, this one removes the conf_target/estimate_mode overloading and introduces a standard fee rate param.

    #20250 keeps the current overloading and makes it more consistent between the six RPCs. Some of what it does (feeRate -> fee_rate) is compatible or orthogonal.

    ISTM the question is, do we want to release with the overloading or with a standard feerate param. (It would be simpler if we decide before the branch-off. After, this PR would need to be changed as we would have to support both.)

  6. DrahtBot commented at 10:09 am on November 5, 2020: member

    The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

    Conflicts

    Reviewers, this pull request conflicts with the following ones:

    • #20391 (wallet: introduce setfeerate (an improved settxfee, in sat/vB) by jonatack)
    • #20362 (test: Implicitly sync after generate* to preempt races and intermittent test failures by MarcoFalke)
    • #18418 (wallet: Increase OUTPUT_GROUP_MAX_ENTRIES to 100 by fjahr)
    • #17526 (Use Single Random Draw In addition to knapsack as coin selection fallback by achow101)
    • #17331 (Use effective values throughout coin selection by achow101)
    • #17211 (Allow fundrawtransaction and walletcreatefundedpsbt to take external inputs by achow101)

    If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

  7. MarcoFalke added this to the milestone 0.21.0 on Nov 5, 2020
  8. jonatack force-pushed on Nov 6, 2020
  9. luke-jr commented at 7:26 pm on November 6, 2020: member

    AFAICT, the only “fee_rate” in 0.20 was bumpfee. Specifying BTC/vkB (current value) in place of the new sat/vB would always be a much lower feerate. So it’d most likely be too low and error, or worst case lower than you intended and you can just bump it again to fix.

    With that in mind, I think the option here should just be renamed to “fee_rate” and break the compatibility for bumpfee.

  10. jonatack commented at 7:42 pm on November 6, 2020: member
    @luke-jr SGTM and that would further simplify the implementation too.
  11. achow101 commented at 10:14 pm on November 6, 2020: member

    Agree with renaming to fee_rate. However having two fee rate options for some RPCs is kinda weird. I suppose it’s too late to fix that for this release, but it’d be nice to not do that in the future.

    Concept ACK.

  12. meshcollider commented at 6:13 am on November 7, 2020: contributor
    Concept ACK for fee_rate
  13. promag commented at 1:11 am on November 9, 2020: member
    Why fee_rate_sat_vb and not just fee_rate? Also, should not change verbose parameter index?
  14. jonatack commented at 10:42 am on November 9, 2020: member

    Why fee_rate_sat_vb and not just fee_rate?

    I initially began with fee_rate in #20231. Then, based on #20220 (review) I changed to fee_rate_sat_vb here. Today, based on the feedback at last Friday’s wallet meeting and here, I will push an update to use fee_rate.

    Also, should not change verbose parameter index?

    For sendtoaddress and sendmany, I hesitated on this because I thought that users are used to seeing the verbose argument placed last, e.g. like gettransaction. If positional true or false are passed for the fee_rate, a type error will be raised and the transaction won’t proceed. Is it better to put fee_rate after verbose instead?

  15. MarcoFalke commented at 10:51 am on November 9, 2020: member
    Has verbose been in a release already?
  16. jonatack commented at 11:19 am on November 9, 2020: member
    Per d5863c0b3e it looks like verbose was added to sendtoaddress and sendmany after the 0.20 release. Not a bugfix, so probably not backported. Checking. Edit: nope, they aren’t in 0.20.0 or 0.20.1. We’re good.
  17. jonatack renamed this:
    wallet: introduce fee_rate_sat_vb param/option
    wallet: introduce fee_rate param/option
    on Nov 9, 2020
  18. jonatack force-pushed on Nov 9, 2020
  19. jonatack renamed this:
    wallet: introduce fee_rate param/option
    wallet: introduce fee_rate sat/vB param/option
    on Nov 9, 2020
  20. jonatack force-pushed on Nov 9, 2020
  21. in src/wallet/rpcwallet.cpp:4090 in 389236d63e outdated
    4086@@ -4087,7 +4087,7 @@ static RPCHelpMan send()
    4087             if (!wallet) return NullUniValue;
    4088             CWallet* const pwallet = wallet.get();
    4089 
    4090-            UniValue options = request.params[3];
    4091+            UniValue options{request.params[3].isNull() ? UniValue::VOBJ : request.params[3]};
    


    Xekyo commented at 4:31 pm on November 9, 2020:
    I get the impression looking around in the codebase that this writes an empty Object, but it’s not clear to me how the value gets populated. I assume that this happens in another place, and it was previously failing because of this object being null instead of empty?

    jonatack commented at 7:15 pm on November 9, 2020:

    Yes, that was my read on this too. And if options is empty, it is populated from the arg params a few lines after:

    0            } else {
    1                options.pushKV("conf_target", request.params[1]);
    2                options.pushKV("estimate_mode", request.params[2]);
    3                options.pushKV("fee_rate", request.params[3]);
    4            }
    
  22. in test/functional/wallet_send.py:285 in 389236d63e outdated
    284@@ -282,17 +285,15 @@ def run_test(self):
    285         for mode in ["foo", Decimal("3.141592")]:
    


    Xekyo commented at 5:07 pm on November 9, 2020:
    Actual, I was just thinking… wouldn’t an empty string be an interesting test case for “mode” as well?

    jonatack commented at 8:50 am on November 10, 2020:

    Good idea, updated each of the test files.

    0-        for mode in ["foo", Decimal("3.141592")]:
    1+        for mode in ["", "foo", Decimal("3.141592")]:
    
  23. in src/wallet/rpcwallet.cpp:219 in 0f7df6b2e8 outdated
    220-            fee_rate /= WALLET_BTC_KB_TO_SAT_B;
    221-        }
    222-
    223-        cc.m_feerate = CFeeRate(fee_rate);
    224+        CAmount feerate{AmountFromValue(estimate_param)};
    225+        cc.m_feerate = cc.m_fee_mode == FeeEstimateMode::SAT_B ? CFeeRate(feerate, COIN) : CFeeRate(feerate);
    


    Xekyo commented at 5:23 pm on November 9, 2020:

    It’s not immediately obvious to me why CFeeRate(feerate, COIN) is equivalent to fee_rate /= WALLET_BTC_KB_TO_SAT_B; CFeeRate(fee_rate). As far as I see, COIN equals 1e8, but WALLET_BTC_KB_TO_SAT_B equals 1e5.

    Upon inspection of ./src/policy/feerate.cpp and ./src/policy/feerate.h, CFeeRate(…) with two arguments appears to construct a fee rate in sat/kvB from fees paid in sats divided by transaction weight in vB:

    0    /** Constructor for a fee rate in satoshis per kB. The size in bytes must not exceed (2^63 - 1)*/
    1    CFeeRate(const CAmount& nFeePaid, size_t nBytes);
    
    0CFeeRate::CFeeRate(const CAmount& nFeePaid, size_t nBytes_)
    1{
    2    assert(nBytes_ <= uint64_t(std::numeric_limits<int64_t>::max()));
    3    int64_t nSize = int64_t(nBytes_);
    4
    5    if (nSize > 0)
    6        nSatoshisPerK = nFeePaid * 1000 / nSize;
    7    else
    8        nSatoshisPerK = 0;
    9}
    

    and either results in the feerate having been divided by 100,000 (1e5). :heavy_check_mark:


    jonatack commented at 7:10 pm on November 9, 2020:
    Yes :+1: and I was more confident about making this change after all the test coverage we added in #20220.

    achow101 commented at 5:12 pm on November 10, 2020:
    It would be nice to have a comment explaining this because it is not immediately clear why this works and future work on this code may not see this explanation.

    jonatack commented at 8:37 pm on November 10, 2020:
    Done, added Doxygen documentation in a3eac6e603fddeda1dae20fba840d42f02710531

    Sjors commented at 7:36 pm on November 12, 2020:
    This feels like a hack, unless I misunderstand the original purpose of this constructor. Maybe it’s better to add a separate constructor with a boolean to distinguish SAT_VB from BTC/kvB.

    jonatack commented at 8:57 pm on November 12, 2020:
    I asked myself why else the two-argument ctor might have been written and didn’t come up with an answer…I’m not against reviewing a refactoring of it.

    jonatack commented at 9:52 pm on November 13, 2020:

    Looking at settxfee in wallet/rpcwallet.cpp, it uses this ctor in a similar manner.

    0    CAmount nAmount = AmountFromValue(request.params[0]);
    1    CFeeRate tx_fee_rate(nAmount, 1000);
    

    jonatack commented at 7:05 pm on November 28, 2020:

    This feels like a hack, unless I misunderstand the original purpose of this constructor. Maybe it’s better to add a separate constructor with a boolean to distinguish SAT_VB from BTC/kvB. @Sjors done, proposed a separate constructor in https://github.com/bitcoin/bitcoin/commit/9c479bfc293fec9063acb9667ddb3cb60b46469d

  24. in src/wallet/rpcwallet.cpp:201 in 53b35be8f8 outdated
    197@@ -198,30 +198,29 @@ static std::string LabelFromValue(const UniValue& value)
    198  *
    199  * @param[in]     pwallet        Wallet pointer
    200  * @param[in,out] cc             Coin control which is to be updated
    201- * @param[in]     estimate_mode  String value (e.g. "ECONOMICAL")
    202- * @param[in]     estimate_param Parameter (blocks to confirm, explicit fee rate, etc)
    203- * @throws a JSONRPCError if estimate_mode is unknown, or if estimate_param is missing when required
    204+ * @param[in]     conf_target    UniValue integer, confirmation target in blocks
    


    Xekyo commented at 5:37 pm on November 9, 2020:
    I think it would be nice to add the permitted value range here.

    jonatack commented at 9:30 am on November 10, 2020:

    Good idea, addressed this and #20305 (review) in 136234cb with

     0diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
     1index edd7efb964..238d37ae98 100644
     2--- a/src/wallet/rpcwallet.cpp
     3+++ b/src/wallet/rpcwallet.cpp
     4@@ -198,30 +198,33 @@ static std::string LabelFromValue(const UniValue& value)
     5  *
     6  * [@param](/bitcoin-bitcoin/contributor/param/)[in]     pwallet        Wallet pointer
     7  * [@param](/bitcoin-bitcoin/contributor/param/)[in,out] cc             Coin control which is to be updated
     8- * [@param](/bitcoin-bitcoin/contributor/param/)[in]     estimate_mode  String value (e.g. "ECONOMICAL")
     9- * [@param](/bitcoin-bitcoin/contributor/param/)[in]     estimate_param Parameter (blocks to confirm, explicit fee rate, etc)
    10- * [@throws](/bitcoin-bitcoin/contributor/throws/) a JSONRPCError if estimate_mode is unknown, or if estimate_param is missing when required
    11+ * [@param](/bitcoin-bitcoin/contributor/param/)[in]     conf_target    UniValue integer, confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h;
    12+ *                                   if a fee_rate is present, 0 is allowed here as a no-op positional placeholder
    13+ * [@param](/bitcoin-bitcoin/contributor/param/)[in]     estimate_mode  UniValue string, fee estimation mode, valid values are "unset", "economical" or "conservative";
    14+ *                                   if a fee_rate is present, "" is allowed here as a no-op positional placeholder
    15+ * [@param](/bitcoin-bitcoin/contributor/param/)[in]     fee_rate       UniValue real, fee rate in sat/vB;
    16+ *                                   if a fee_rate is present, both conf_target and estimate_mode must either be null, or no-op values
    17+ * [@throws](/bitcoin-bitcoin/contributor/throws/) a JSONRPCError if conf_target, estimate_mode, or fee_rate contain invalid values or are in conflict
    

    Xekyo commented at 7:19 pm on November 10, 2020:
    Excellent, I like this very thorough comment.
  25. in src/wallet/rpcwallet.cpp:203 in 53b35be8f8 outdated
    197@@ -198,30 +198,29 @@ static std::string LabelFromValue(const UniValue& value)
    198  *
    199  * @param[in]     pwallet        Wallet pointer
    200  * @param[in,out] cc             Coin control which is to be updated
    201- * @param[in]     estimate_mode  String value (e.g. "ECONOMICAL")
    202- * @param[in]     estimate_param Parameter (blocks to confirm, explicit fee rate, etc)
    203- * @throws a JSONRPCError if estimate_mode is unknown, or if estimate_param is missing when required
    204+ * @param[in]     conf_target    UniValue integer, confirmation target in blocks
    205+ * @param[in]     estimate_mode  UniValue string, fee estimation mode ("unset", "economical" or "conservative")
    206+ * @param[in]     fee_rate       UniValue real, fee rate in sat/vB
    


    Xekyo commented at 5:40 pm on November 9, 2020:
    Since setting fee_rate is incompatible with a conf_target or estimation_mode, it might be nice to mention this here in the description.

    jonatack commented at 9:31 am on November 10, 2020:
    :+1: done as described in #20305 (review)
  26. in src/wallet/rpcwallet.cpp:216 in 53b35be8f8 outdated
    211-    if (!estimate_mode.isNull()) {
    212-        if (!FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
    213-            throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
    214+    if (!fee_rate.isNull()) {
    215+        if (!conf_target.isNull() && conf_target.get_int() > 0) { // conf_target value of 0 allowed as positional placeholder
    216+            throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.");
    


    Xekyo commented at 5:47 pm on November 9, 2020:
    Since setting fee_rate is incompatible with a conf_target or estimation_mode, but they are provided as positional arguments via the RPC, it might be nice to mention the correct positional arguments for the latter two in this error message.

    jonatack commented at 9:28 pm on November 9, 2020:
    The positional order depends on the RPC, so the user would need to refer to the specific RPC help. I tried to generally keep to the following positional order, where possible: conf_target, estimate_mode, fee_rate.
  27. in src/wallet/rpcwallet.cpp:220 in 53b35be8f8 outdated
    234-    } else if (!estimate_param.isNull()) {
    235-        cc.m_confirm_target = ParseConfirmTarget(estimate_param, pwallet->chain().estimateMaxBlocks());
    236+        return;
    237+    }
    238+    if (!estimate_mode.isNull() && !FeeModeFromString(estimate_mode.get_str(), cc.m_fee_mode)) {
    239+        throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid estimate_mode parameter");
    


    Xekyo commented at 5:49 pm on November 9, 2020:
    If I were a user, I would like to get instructions about what the valid parameters are, if that’s not too much work.

    jonatack commented at 11:51 am on November 10, 2020:
    Good idea, doing.

    jonatack commented at 8:40 pm on November 10, 2020:

    If I were a user, I would like to get instructions about what the valid parameters are, if that’s not too much work.

    Done in b827d2dbc229a

  28. in src/wallet/rpcwallet.cpp:3144 in 53b35be8f8 outdated
    3121@@ -3120,15 +3122,21 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
    3122             lockUnspents = (options.exists("lock_unspents") ? options["lock_unspents"] : options["lockUnspents"]).get_bool();
    3123         }
    3124 
    3125-        if (options.exists("feeRate"))
    3126-        {
    3127+        if (options.exists("feeRate")) {
    3128+            if (options.exists("fee_rate")) {
    3129+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both fee_rate (" + CURRENCY_ATOM + "/vB) and feeRate (" + CURRENCY_UNIT + "/kvB)");
    


    Xekyo commented at 5:52 pm on November 9, 2020:

    haha, the horrors. :scream:

    This has a confusion potential like me being confused about the order of arguments between find and grep every single time.


    jonatack commented at 7:03 pm on November 9, 2020:
    Yes, reckon we should deprecate feeRate as soon as feasible after this.

    jonatack commented at 9:56 pm on November 24, 2020:

    Yes, reckon we should deprecate feeRate as soon as feasible after this.

    Done in #20483

  29. in src/wallet/rpcwallet.cpp:3153 in 53b35be8f8 outdated
    3135             if (options.exists("estimate_mode")) {
    3136                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
    3137             }
    3138-            coinControl.m_feerate = CFeeRate(AmountFromValue(options["feeRate"]));
    3139+            CFeeRate fee_rate(AmountFromValue(options["feeRate"]));
    3140+            if (fee_rate <= CFeeRate(0)) {
    


    Xekyo commented at 5:53 pm on November 9, 2020:
    But just because being able to ask a miner to pay for including a transaction would be such a terrible DOS vector. :stuck_out_tongue_winking_eye:

    MarcoFalke commented at 6:52 pm on November 17, 2020:
    This is a regression. In 0.20 it is possible to fund a raw tx without fee

    jonatack commented at 4:03 pm on November 19, 2020:

    It is possible to call fundrawtransaction (or walletcreatefundedpsbt) without passing a feeRate or a fee_rate, as they are optional args.

    Behavior before a0d495747320c79b27a83c216dcc526ac8df8f24: a negative feeRate raised “Amount out of range”, and passing a feeRate of zero was possible in these two calls, but not in bumpfee or send{toaddress, many}.

    Current behavior is the same except that the > 0 feeRate check was extended to fundraw and WCFP.

    I will remove the zero check on feeRate/fee_rate for those two calls.


    jonatack commented at 4:11 pm on November 19, 2020:
    Discussing offline with @harding and @Xekyo, zero-fee valid txns used to be a thing and might make sense in some future situations (package relay, LN anchor commitments) even if they are non-standard.

    MarcoFalke commented at 5:02 pm on November 19, 2020:
    “zero-fee” txs could also pay fees externally (out-of-band), not even in the same package

    jonatack commented at 6:24 pm on November 19, 2020:
    Done in #20426
  30. in src/wallet/rpcwallet.cpp:3401 in 53b35be8f8 outdated
    3395@@ -3386,16 +3396,16 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
    3396                 {
    3397                     {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target (in blocks)\n"
    3398                              "or fee rate (for " + CURRENCY_UNIT + "/kB and " + CURRENCY_ATOM + "/B estimate modes)"},
    3399-                    {"fee_rate", RPCArg::Type::NUM, /* default */ "fall back to 'conf_target'", "fee rate (NOT total fee) to pay, in " + CURRENCY_UNIT + "/kB.\n"
    3400-                             "Specify a fee rate instead of relying on the built-in fee estimator.\n"
    3401-                             "Must be at least 0.0001 " + CURRENCY_UNIT + "/kB higher than the current transaction fee rate.\n"},
    3402+                    {"fee_rate", RPCArg::Type::AMOUNT, /* default */ "not set, fall back to wallet fee estimation",
    3403+                             "\nSpecify a fee rate in " + CURRENCY_ATOM + "/vB instead of relying on the built-in fee estimator.\n"
    3404+                             "Must be at least 1 " + CURRENCY_ATOM + "/vB higher than the current transaction fee rate.\n"},
    


    Xekyo commented at 5:58 pm on November 9, 2020:
    I think strictly speaking this is tied to MIN_RELAY_TX_FEE, so it might be nice to tie it to that instead of hardcoding to satoshi.

    jonatack commented at 8:41 pm on November 10, 2020:
    Done in 802193e73e22580ec77
  31. in src/wallet/rpcwallet.cpp:4099 in 53b35be8f8 outdated
    4099-            if (options.exists("feeRate") || options.exists("fee_rate") || options.exists("estimate_mode") || options.exists("conf_target")) {
    4100-                if (!request.params[1].isNull() || !request.params[2].isNull()) {
    4101-                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Use either conf_target and estimate_mode or the options dictionary to control fee rate");
    4102+            UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
    4103+            if (options.exists("conf_target") || options.exists("estimate_mode") || options.exists("fee_rate")) {
    4104+                if (!request.params[1].isNull() || !request.params[2].isNull() || !request.params[3].isNull()) {
    


    Xekyo commented at 6:06 pm on November 9, 2020:
    Maybe I’m missing something, but I had trouble figuring out from the local code what the parameter set of this particular call is here. If I understand correctly, their types are being defined in 4086-4089. Maybe the parameter names could be added as comments there or here to remind the reader what is being tested to be null here in 4099.

    Xekyo commented at 6:08 pm on November 9, 2020:
    I was trying to figure out what exactly is checked here that prevents e.g. both conf_target and fee_rate to be set here, if params[1]…[3] all need to be null here.

    jonatack commented at 2:08 pm on November 10, 2020:

    Added these comments:

     0             RPCTypeCheck(request.params, {
     1-                UniValueType(), // ARR or OBJ, checked later
     2-                UniValue::VNUM,
     3-                UniValue::VSTR,
     4-                UniValue::VNUM,
     5-                UniValue::VOBJ,
     6+                UniValueType(), // outputs (ARR or OBJ, checked later)
     7+                UniValue::VNUM, // conf_target
     8+                UniValue::VSTR, // estimate_mode
     9+                UniValue::VNUM, // fee_rate
    10+                UniValue::VOBJ, // options
    11                 }, true
    12             );
    

    I was trying to figure out what exactly is checked here that prevents e.g. both conf_target and fee_rate to be set here, if params[1]…[3] all need to be null here.

    You’re right. Updated.

     0             UniValue options{request.params[4].isNull() ? UniValue::VOBJ : request.params[4]};
     1-            if (options.exists("conf_target") || options.exists("estimate_mode") || options.exists("fee_rate")) {
     2-                if (!request.params[1].isNull() || !request.params[2].isNull() || !request.params[3].isNull()) {
     3-                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Use either conf_target and estimate_mode for fee estimation, or fee_rate to specify an explicit fee rate.");
     4+            if (options.exists("conf_target") || options.exists("estimate_mode")) {
     5+                if (!request.params[1].isNull() || !request.params[2].isNull()) {
     6+                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Pass conf_target and estimate_mode either as arguments or in the options object, but not both");
     7                 }
     8             } else {
     9                 options.pushKV("conf_target", request.params[1]);
    10                 options.pushKV("estimate_mode", request.params[2]);
    11+            }
    12+            if (options.exists("fee_rate")) {
    13+                if (!request.params[3].isNull()) {
    14+                    throw JSONRPCError(RPC_INVALID_PARAMETER, "Pass fee_rate either as an argument or in the options object, but not both");
    15+                }
    16+            } else {
    17                 options.pushKV("fee_rate", request.params[3]);
    18             }
    

    This hopefully maintains the original goal of the code that either the argument or the option should be passed, but not both. The check if conf_target or estimate_mode are passed at the same time as fee_rate happens in SetFeeEstimateMode().


    jonatack commented at 2:16 pm on November 10, 2020:

    Updated wallet_send tests.

     0+++ b/test/functional/wallet_send.py
     1@@ -234,7 +234,7 @@ class WalletSendTest(BitcoinTestFramework):
     2         for mode in ["unset", "economical", "conservative"]:
     3             self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=1, arg_estimate_mode="economical",
     4                 conf_target=1, estimate_mode=mode, add_to_wallet=False,
     5-                expect_error=(-8, "Use either conf_target and estimate_mode for fee estimation, or fee_rate to specify an explicit fee rate."))
     6+                expect_error=(-8, "Pass conf_target and estimate_mode either as arguments or in the options object, but not both"))
     7 
     8         self.log.info("Create PSBT from watch-only wallet w3, sign with w2...")
     9         res = self.test_send(from_wallet=w3, to_wallet=w1, amount=1)
    10@@ -278,6 +278,10 @@ class WalletSendTest(BitcoinTestFramework):
    11         fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
    12         assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.00003"))
    13 
    14+        # Test that passing fee_rate as both an argument and an option raises.
    15+        self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=1, fee_rate=1, add_to_wallet=False,
    16+                       expect_error=(-8, "Pass fee_rate either as an argument or in the options object, but not both"))
    

    Xekyo commented at 7:33 pm on November 10, 2020:
    Ooooh, so this was checking that not both the options and arguments were being passed. I guess I hadn’t fully grokked that. I guess that would have been fine then, if any of the three was set on one, it would be incorrect for the any of the others to be set in the other parameter set. It actually is a bit more lenient locally then, but since e.g. passing both estimate_mode and fee_rate would throw in other places this is not a problem.

    jonatack commented at 8:42 pm on November 10, 2020:
    Thanks to your feedback, the error messages seem clearer now.
  32. in src/wallet/rpcwallet.cpp:4111 in 53b35be8f8 outdated
    4111             }
    4112             if (!options["conf_target"].isNull() && (options["estimate_mode"].isNull() || (options["estimate_mode"].get_str() == "unset"))) {
    4113                 throw JSONRPCError(RPC_INVALID_PARAMETER, "Specify estimate_mode");
    4114             }
    4115+            if (options.exists("feeRate")) {
    4116+                throw JSONRPCError(RPC_INVALID_PARAMETER, "Use fee_rate instead of feeRate");
    


    Xekyo commented at 6:09 pm on November 9, 2020:
    Optional: mention that fee_rate is specified in [sat/vB] other than feeRate.

    jonatack commented at 8:43 pm on November 10, 2020:
    Done in 136234cb
  33. in test/functional/rpc_fundrawtransaction.py:719 in 53b35be8f8 outdated
    723-        assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)", self.nodes[3].fundrawtransaction, rawtx, {"feeRate": 1})
    724+        outputs = {node.getnewaddress() : 1}
    725+        rawtx = node.createrawtransaction(inputs, outputs)
    726+
    727+        result = node.fundrawtransaction(rawtx)  # uses self.min_relay_tx_fee (set by settxfee)
    728+        result1 = node.fundrawtransaction(rawtx, {"fee_rate": 200000 * self.min_relay_tx_fee})
    


    Xekyo commented at 6:18 pm on November 9, 2020:

    I got hung up on this line because I first thought that minRelayTxFee would be 1,000 sats/kvB, but thinking about it, I assume that self.min_relay_tx_fee is specified in BTC/kvB. I did notice that this does not seem to ever be explicitly stated in this file.

    So, the number just sort of comes from nowhere.


    jonatack commented at 3:21 pm on November 10, 2020:

    Updated in 136234cb

    0         result = node.fundrawtransaction(rawtx)  # uses self.min_relay_tx_fee (set by settxfee)
    1-        result1 = node.fundrawtransaction(rawtx, {"fee_rate": 200000 * self.min_relay_tx_fee})
    2+        btc_kvb_to_sat_vb = 100000  # (1e5)
    3+        result1 = node.fundrawtransaction(rawtx, {"fee_rate": 2 * btc_kvb_to_sat_vb * self.min_relay_tx_fee})
    4         result2 = node.fundrawtransaction(rawtx, {"feeRate": 2 * self.min_relay_tx_fee})
    5-        result3 = node.fundrawtransaction(rawtx, {"fee_rate": 1000000 * self.min_relay_tx_fee})
    6+        result3 = node.fundrawtransaction(rawtx, {"fee_rate": 10 * btc_kvb_to_sat_vb * self.min_relay_tx_fee})
    7         result4 = node.fundrawtransaction(rawtx, {"feeRate": 10 * self.min_relay_tx_fee})
    
  34. in test/functional/rpc_fundrawtransaction.py:733 in 53b35be8f8 outdated
    758-        for field, fee_rate in {"conf_target": 0.1, "estimate_mode": "sat/b"}.items():
    759-            self.log.info("Test fundrawtxn raises RPC error if both feeRate and {} are passed".format(field))
    760-            assert_raises_rpc_error(
    761-                -8, "Cannot specify both {} and feeRate".format(field),
    762-                lambda: node.fundrawtransaction(rawtx, {"feeRate": 0.1, field: fee_rate}))
    763+        # With no arguments passed, expect fee of 141 sats/b.
    


    Xekyo commented at 6:22 pm on November 9, 2020:
    I think this may be a fee so the unit should just be satoshi, not sats/B. Otherwise, I’d not expect the result below to be 10,000×, but rather at a factor of 10,000/141. :thinking:

    jonatack commented at 3:25 pm on November 10, 2020:
    Right! Done in 136234cb
  35. in test/functional/rpc_fundrawtransaction.py:735 in 53b35be8f8 outdated
    760-            assert_raises_rpc_error(
    761-                -8, "Cannot specify both {} and feeRate".format(field),
    762-                lambda: node.fundrawtransaction(rawtx, {"feeRate": 0.1, field: fee_rate}))
    763+        # With no arguments passed, expect fee of 141 sats/b.
    764+        assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000141, vspan=0.00000001)
    765+        # Expect fee to be 10,000x higher when explicit fee 10,000x greater is specified.
    


    Xekyo commented at 6:30 pm on November 9, 2020:

    This would in turn mean that this sentence should be:

    Expect fee to be 10,000x higher when explicit feerate 10,000x greater is specified.

    Also, nit: Wouldn’t “10,000× higher” be “10,001 as high”? :nerd_face:


    jonatack commented at 3:24 pm on November 10, 2020:

    Well-spotted, done in 136234cb

    0-        # With no arguments passed, expect fee of 141 sats/b.
    1+        # With no arguments passed, expect fee of 141 satoshis.
    2         assert_approx(node.fundrawtransaction(rawtx)["fee"], vexp=0.00000141, vspan=0.00000001)
    3-        # Expect fee to be 10,000x higher when explicit fee 10,000x greater is specified.
    4+        # Expect fee to be 10,000x higher when explicit fee rate 10,000x greater is specified.
    
  36. in test/functional/rpc_fundrawtransaction.py:762 in 53b35be8f8 outdated
    805+        self.log.info("Test invalid fee rate settings")
    806+        assert_raises_rpc_error(-4, "Fee rate (0.00000000 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
    807+            node.fundrawtransaction, rawtx, {"fee_rate": 0, "add_inputs": True})
    808+        assert_raises_rpc_error(-8, "Invalid fee_rate 0.00000000 BTC/kB (must be greater than 0)",
    809+            node.fundrawtransaction, rawtx, {"feeRate": 0, "add_inputs": True})
    810+        for param, value in {("fee_rate", 100000), ("feeRate", 1.1)}:
    


    Xekyo commented at 6:38 pm on November 9, 2020:
    I’m not sure if we did that already, but it would be good to assure that the valid value range for fee_rate will cause an error for feeRate and vice versa. I would expect a lot of people to plug values for either into the other, and I’m not sure off the top of my head that the ranges have no overlap. (They should be shifted by a factor of 1e5 though, right?)

    jonatack commented at 3:03 pm on November 10, 2020:

    Good point. Yes, 1e5 apart. The ranges don’t overlap if I’m not confused, except in the minimum fee direction. We already check the min/max for fee_rate (0.1-100,000 sat/vB) and the max for feeRate (1 BTC/kvB) in the functional tests. At the moment, min fee checks with feeRate are disabled due to coinControl.fOverrideFeeRate = true in wallet/rpcwallet.cpp FundTransaction().

    To test the minimum for feeRate (0.00000999 BTC/kvB), we’d have to remove that fOverrideFeeRate setting. I’ve added this for now:

    0test/functional/rpc_fundrawtransaction.py
    1@@ 772 @@ class RawTransactionsTest(BitcoinTestFramework):
    2+        # This feeRate test only passes if `coinControl.fOverrideFeeRate = true` in wallet/rpcwallet.cpp::FundTransaction is removed.
    3+        assert_raises_rpc_error(-4, "Fee rate (0.999 sat/vB) is lower than the minimum fee rate setting (1.000 sat/vB)",
    4+            node.fundrawtransaction, rawtx, {"feeRate": 0.00000999, "add_inputs": True})
    

    jonatack commented at 11:36 am on November 11, 2020:

    It’s not clear to me if fOverrideFeeRate should be set to true if fee_rate is passed in fundrawtransaction or walletcreatefundedpsbt, as it is for feeRate, or if the fOverrideFeeRate flag could just be removed.

    AFAICT fOverrideFeeRate set to true currently only serves to bypass minimum feerate checks for feeRate in GetMinimumFeeRate() for fundrawtransaction and walletcreatefundedpsbt.


    jonatack commented at 7:16 pm on November 11, 2020:
    Found this mini-discussion in #10706 in mid-2017 between @ryanofsky and @morcos about the use of fOverrideFeeRate for feeRate in fundrawtransaction (and now also walletcreatefundedpsbt).

    jonatack commented at 10:31 am on November 12, 2020:
    Decided to maintain the fOverrideFeeRate behavior of disabling the min fee checks on fee_rate in RPCs fundrawtransaction and walletcreatefundedpsbt only (there is a “fee rate greater than zero” check instead), as has been the case for feeRate in these two RPCs since 2017, per the above comments. Done in 05e82d86b09d914ebce05dbc92a7299cb026847b.
  37. in test/functional/rpc_psbt.py:207 in 53b35be8f8 outdated
    211+        assert_raises_rpc_error(-4, "Fee rate (0.00000000 BTC/kB) is lower than the minimum fee rate setting (0.00001000 BTC/kB)",
    212+            self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"fee_rate": 0, "add_inputs": True})
    213+        assert_raises_rpc_error(-8, "Invalid fee_rate 0.00000000 BTC/kB (must be greater than 0)",
    214+            self.nodes[1].walletcreatefundedpsbt, inputs, outputs, 0, {"feeRate": 0, "add_inputs": True})
    215+        for param, value in {("fee_rate", 100000), ("feeRate", 1.1)}:
    216+            assert_raises_rpc_error(-4, "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)",
    


    Xekyo commented at 6:41 pm on November 9, 2020:
    Probably out of scope, but it would be kinda nice if an error like this told the user what said limit is.

    jonatack commented at 3:28 pm on November 10, 2020:
    Agree–punting on this for the next push but would be good.
  38. in test/functional/wallet_basic.py:235 in 53b35be8f8 outdated
    276-            conf_target=fee_sat_per_b,
    277-            estimate_mode='sAT/b',
    278-        )
    279+        self.log.info("Test sendmany with fee_rate param (explicit fee rate in sat/vB)")
    280+        fee_rate_sat_vb = 2
    281+        fee_rate_btc_kvb = fee_rate_sat_vb / 100000.0
    


    Xekyo commented at 6:46 pm on November 9, 2020:

    I mean, I see where that 1e-5 is coming from, but if one is not as immersed, it may be a bit more readable, if the two conversions were mentioned more explicitly:

    0fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
    

    jonatack commented at 3:30 pm on November 10, 2020:

    Done in 136234cb

     0+++ b/test/functional/wallet_basic.py
     1@@ -232,7 +232,7 @@ class WalletTest(BitcoinTestFramework):
     2 
     3         self.log.info("Test sendmany with fee_rate param (explicit fee rate in sat/vB)")
     4         fee_rate_sat_vb = 2
     5-        fee_rate_btc_kvb = fee_rate_sat_vb / 100000.0
     6+        fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
     7         explicit_fee_rate_btc_kvb = Decimal(fee_rate_btc_kvb) / 1000
     8 
     9         # Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
    10@@ -405,7 +405,7 @@ class WalletTest(BitcoinTestFramework):
    11             address = self.nodes[1].getnewaddress()
    12             amount = 3
    13             fee_rate_sat_vb = 2
    14-            fee_rate_btc_kvb = fee_rate_sat_vb / 100000.0
    15+            fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
    
  39. in test/functional/wallet_basic.py:236 in 53b35be8f8 outdated
    277-            estimate_mode='sAT/b',
    278-        )
    279+        self.log.info("Test sendmany with fee_rate param (explicit fee rate in sat/vB)")
    280+        fee_rate_sat_vb = 2
    281+        fee_rate_btc_kvb = fee_rate_sat_vb / 100000.0
    282+        explicit_fee_rate_vb = Decimal(fee_rate_btc_kvb) / 1000
    


    Xekyo commented at 6:50 pm on November 9, 2020:
    This seems to be the first time we use BTC/vB. Given that it’s a bit uncommon, that should perhaps be part of the variable name: I suggest to use explicit_fee_rate_BTC_vb.

    jonatack commented at 9:14 pm on November 9, 2020:
    Good idea. Renamed to explicit_fee_rate_btc_kvb (since the variable names are lowercase) in 136234cb
  40. in test/functional/wallet_basic.py:414 in 53b35be8f8 outdated
    436-            fee = prebalance - postbalance - Decimal('1')
    437-            assert_fee_amount(fee, tx_size, Decimal('0.00002500'))
    438-
    439-            self.sync_all(self.nodes[0:3])
    440+            fee = prebalance - postbalance - Decimal(amount)
    441+            assert_fee_amount(fee, tx_size, Decimal(fee_rate_sat_vb / 100000.0))
    


    Xekyo commented at 6:55 pm on November 9, 2020:
    Isn’t that Decimal(fee_rate_sat_vb / 100000.0) just a repetition of fee_rate_btc_kvb?

    jonatack commented at 9:11 pm on November 9, 2020:

    Yup, good eye. One tests sendmany and the other tests sendtoaddress. Made this change in 136234cb

    0             fee_rate_sat_vb = 2
    1+            fee_rate_btc_kvb = fee_rate_sat_vb / 100000.0
    2.../...
    3             fee = prebalance - postbalance - Decimal(amount)
    4-            assert_fee_amount(fee, tx_size, Decimal(fee_rate_sat_vb / 100000.0))
    5+            assert_fee_amount(fee, tx_size, Decimal(fee_rate_btc_kvb))
    
  41. in test/functional/wallet_basic.py:238 in 53b35be8f8 outdated
    278-        )
    279+        self.log.info("Test sendmany with fee_rate param (explicit fee rate in sat/vB)")
    280+        fee_rate_sat_vb = 2
    281+        fee_rate_btc_kvb = fee_rate_sat_vb / 100000.0
    282+        explicit_fee_rate_vb = Decimal(fee_rate_btc_kvb) / 1000
    283+        # Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
    


    Xekyo commented at 6:57 pm on November 9, 2020:
    Okay, this is as far as I got in 53b35be8f87fd894614a5e3609b1d8895a96c93c. I require nourishment. As discussed will continue after lunch.

    jonatack commented at 9:32 pm on November 9, 2020:
    Thanks for the good ideas and feedback, @Xekyo. I’ll try to implement them (and any other feedback you may have while continuing) tomorrow in the morning to move this forward. Heading to bed :)
  42. Xekyo commented at 6:58 pm on November 9, 2020: member
    Partial review up to ~3/4 of 53b35be8f87fd894614a5e3609b1d8895a96c93c. Will continue in approx 60 minutes.
  43. in test/functional/wallet_bumpfee.py:110 in 53b35be8f8 outdated
    110 
    111-        # For each fee mode, bumping to just above minrelay should fail to increase the total fee enough.
    112-        for options in [{"fee_rate": INSUFFICIENT}, {"conf_target": INSUFFICIENT, "estimate_mode": BTC_MODE}, {"conf_target": 1, "estimate_mode": SAT_MODE}]:
    113-            assert_raises_rpc_error(-8, "Insufficient total fee", rbf_node.bumpfee, rbfid, options)
    114+        # Bumping to just above minrelay should fail to increase the total fee enough.
    115+        assert_raises_rpc_error(-8, "Insufficient total fee 0.00000141, must be at least 0.00001704 (oldFee 0.00000999 + incrementalFee 0.00000705)",
    


    Xekyo commented at 1:28 am on November 10, 2020:
    Getting hung up on all the wrong things, but if minRelayTxFee × txsize is 141 sats, I’m surprised that the incrementalFee is 705. I thought that minimum increments are equal to minRelayTxFee.

    jonatack commented at 3:35 pm on November 10, 2020:

    I think that’s from wallet/wallet.h, though the different fee constants and config options are a bit confusing to me.

    0//! minimum recommended increment for BIP 125 replacement txs
    1static const CAmount WALLET_INCREMENTAL_RELAY_FEE = 5000;
    

    and wallet/feebumper.cpp

    0    if (new_total_fee < minTotalFee) {
    1        errors.push_back(strprintf(Untranslated("Insufficient total fee %s, must be at least %s (oldFee %s + incrementalFee %s)"),
    2            FormatMoney(new_total_fee), FormatMoney(minTotalFee), FormatMoney(nOldFeeRate.GetFee(maxTxSize)), FormatMoney(incrementalRelayFee.GetFee(maxTxSize))));
    

    Xekyo commented at 7:36 pm on November 10, 2020:
    Thanks for tracking that down!
  44. in test/functional/wallet_bumpfee.py:116 in 53b35be8f8 outdated
    120-            assert_raises_rpc_error(-8, "Selected estimate_mode {} requires a fee rate to be specified in conf_target".format(unit),
    121-                                    rbf_node.bumpfee, rbfid, {"fee_rate": fee_rate, "estimate_mode": unit})
    122+        self.log.info("Test invalid fee rate settings")
    123+        assert_raises_rpc_error(-8, "Invalid fee_rate 0.00000000 BTC/kB (must be greater than 0)",
    124+            rbf_node.bumpfee, rbfid, {"fee_rate": 0})
    125+        assert_raises_rpc_error(-4, "Specified or calculated fee 0.141 is too high (cannot be higher than -maxtxfee 0.10",
    


    Xekyo commented at 1:31 am on November 10, 2020:
    woah, damn. I think we may want to have a look at the maxtxfee that’s a lot of money.

    jonatack commented at 3:38 pm on November 10, 2020:
    Could be a good follow-up.

    Sjors commented at 8:00 pm on November 12, 2020:
    I proposed that once: #16539

    jonatack commented at 8:54 pm on November 12, 2020:

    I proposed that once: #16539

    TIL, thanks. Interesting discussion in that PR.

  45. in src/wallet/rpcwallet.cpp:3410 in fd2ebb6526 outdated
    3386@@ -3387,9 +3387,10 @@ static RPCHelpMan bumpfee_helper(std::string method_name)
    3387         "The command will fail if the wallet or mempool contains a transaction that spends one of T's outputs.\n"
    3388         "By default, the new fee will be calculated automatically using the estimatesmartfee RPC.\n"
    3389         "The user can specify a confirmation target for estimatesmartfee.\n"
    3390-        "Alternatively, the user can specify a fee_rate (in " + CURRENCY_ATOM + "/vB) for the new transaction.\n"
    3391+        "Alternatively, the user can specify a fee rate in " + CURRENCY_ATOM + "/vB for the new transaction.\n"
    3392         "At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee\n"
    3393-        "returned by getnetworkinfo) to enter the node's mempool.\n",
    3394+        "returned by getnetworkinfo) to enter the node's mempool.\n"
    3395+        "* WARNING: before version 0.21, fee_rate was in " + CURRENCY_UNIT + "/kvB. As of 0.21, fee_rate is in " + CURRENCY_ATOM + "/vB. *\n",
    


    Xekyo commented at 1:40 am on November 10, 2020:
    Luckily, this is very benign. In the worst case, someone is going to get upped to the minRelayTxFee silently and sends at 1 sat/vB. Since RBF is on by default, they should be able to bump when they notice. :+1:
  46. in src/wallet/rpcwallet.cpp:4398 in 822ad02727 outdated
    4362@@ -4369,8 +4363,7 @@ static RPCHelpMan walletcreatefundedpsbt()
    4363                             },
    4364                             {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Marks this transaction as BIP125 replaceable.\n"
    4365                                                           "Allows this transaction to be replaced by a transaction with higher fees"},
    4366-                            {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target (in blocks)\n"
    4367-                                                          "or fee rate (for " + CURRENCY_UNIT + "/kB and " + CURRENCY_ATOM + "/B estimate modes)"},
    4368+                            {"conf_target", RPCArg::Type::NUM, /* default */ "wallet -txconfirmtarget", "Confirmation target in blocks"},
    


    Xekyo commented at 1:41 am on November 10, 2020:
    Overloading, begone! :fireworks:
  47. in src/policy/feerate.h:23 in ced7d54e6d outdated
    18@@ -19,8 +19,8 @@ enum class FeeEstimateMode {
    19     UNSET,        //!< Use default settings based on other criteria
    20     ECONOMICAL,   //!< Force estimateSmartFee to use non-conservative estimates
    21     CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
    22-    BTC_KB,       //!< Use explicit BTC/kB fee given in coin control
    23-    SAT_B,        //!< Use explicit sat/B fee given in coin control
    24+    BTC_KVB,      //!< Use BTC/kvB fee rate unit
    25+    SAT_VB,       //!< Use sat/vB fee rate unit
    


    Xekyo commented at 1:44 am on November 10, 2020:
    Given that we’re introducing sat/vB everywhere, how come this BTC_KVB was kept?

    jonatack commented at 3:42 pm on November 10, 2020:
    It’s still the default unit elsewhere (until the remaining RPCs move to sat/vB) and also needed for the feeRate BTC/kvB error messages. For now, this PR remains in the scope of the 6 RPCs described in the PR description and the “wallet: introduce fee_rate (sat/vB) param/option” commit message.
  48. in src/wallet/rpcwallet.cpp:2325 in cfc14701c6 outdated
    2302@@ -2303,7 +2303,7 @@ static RPCHelpMan settxfee()
    2303                 "\nSet the transaction fee per kB for this wallet. Overrides the global -paytxfee command line parameter.\n"
    2304                 "Can be deactivated by passing 0 as the fee. In that case automatic fee selection will be used by default.\n",
    2305                 {
    2306-                    {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee in " + CURRENCY_UNIT + "/kB"},
    2307+                    {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The transaction fee in " + CURRENCY_UNIT + "/kvB"},
    


    Xekyo commented at 1:51 am on November 10, 2020:

    I assume that you considered and deliberately passed on also replacing these. I would surmise that it’s it just too much of a can of worms? Otherwise it would be kinda odd that we are using sat/vB for the fee rate in all the send variants, but then use BTC/kvB for settxfee.

    Nit: settxfee sets not a fee, but a feerate. ;)


    jonatack commented at 3:45 pm on November 10, 2020:
    Yep, scope creep on an already-wide PR. Could be a next step.

    jonatack commented at 10:26 pm on November 14, 2020:
    Done in #20391.
  49. in src/wallet/rpcwallet.cpp:460 in 5660dd3011 outdated
    461-            + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" false true 2 " + (CURRENCY_ATOM + "/B"))
    462-            + HelpExampleRpc("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 0.1, \"donation\", \"seans outpost\"")
    463+                    "\nSend 0.1 BTC\n"
    464+                    + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1") +
    465+                    "\nSend 0.1 BTC with a confirmation target of 6 blocks in economical fee estimate mode using positional arguments\n"
    466+                    + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"lunch\" \"seans outpost\" false true 6 economical") +
    


    Xekyo commented at 1:58 am on November 10, 2020:
    To explain why that was “donation”: “Sean’s Outpost” was a homeless outreach program based in Florida that was run by a bitcoiner and was somewhat known in the Bitcoin ecosystem in ~2013-2014.

    jonatack commented at 8:35 am on November 10, 2020:
    TIL, thanks! Fixing
  50. in src/wallet/rpcwallet.cpp:4089 in 5660dd3011 outdated
    4072+        + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' 0 \"\" 1\n") +
    4073+        "Send 0.2 BTC with a fee rate of 1 " + CURRENCY_ATOM + "/vB using the options argument\n"
    4074+        + HelpExampleCli("send", "'{\"" + EXAMPLE_ADDRESS[0] + "\": 0.2}' '{\"fee_rate\": 1}'\n") +
    4075+        "Send 0.3 BTC with a fee rate of 25 " + CURRENCY_ATOM + "/vB using named arguments\n"
    4076+        + HelpExampleCli("-named send", "outputs='{\"" + EXAMPLE_ADDRESS[0] + "\": 0.3}' fee_rate=25\n") +
    4077+        "Create a transaction that should confirm the next block, with a specific input, and return result without adding to wallet or broadcasting to the network\n"
    


    Xekyo commented at 2:02 am on November 10, 2020:
    Thanks for updating all these! I think they hadn’t been filled-in since 2013ish given the above mention of Sean’s Outpost :)
  51. Xekyo commented at 2:03 am on November 10, 2020: member
    Looks great to me! Gonna circle back tomorrow after I get up. :)
  52. achow101 commented at 6:11 pm on November 10, 2020: member
    ACK 5660dd301152c0c7a70934cc7650ad162a44c9b4
  53. jonatack force-pushed on Nov 10, 2020
  54. jonatack commented at 8:55 pm on November 10, 2020: member
    Addressed @Xekyo and @achow101 feedback (thanks!), fixed a few things seen while self-reviewing (and removed a few duplicate tests/added some missing tests in rpc_fundrawtransaction.py) per git diff 5660dd3 bcb8e0d 🍰✨
  55. jonatack closed this on Nov 11, 2020

  56. jonatack reopened this on Nov 11, 2020

  57. jonatack commented at 1:35 am on November 11, 2020: member
    Closed and reopened to see if the valgrind fuzzing ci will stop timing out and to try to kick appveyor into working. :hammer: The PR is all green on https://bitcoinbuilds.org/?build=4300.
  58. jonatack force-pushed on Nov 11, 2020
  59. wallet: fix bug in RPC send options
    when empty, options were not being populated by arguments of the same name
    
    found while adding test coverage in 603c0050
    3f72791613
  60. wallet: add CFeeRate ctor doxygen documentation
    as requested by reviewers
    6112cf20d4
  61. wallet: remove unneeded WALLET_BTC_KB_TO_SAT_B constant e21212f01b
  62. wallet: introduce fee_rate (sat/vB) param/option
    Create a fee_rate (sat/vB) RPC param and replace overloading the conf_target and
    estimate_mode params in the following 6 RPCs with it:
    
    - sendtoaddress
    - sendmany
    - send
    - fundrawtransaction
    - walletcreatefundedpsbt
    - bumpfee
    
    In RPC bumpfee, the previously existing fee_rate remains but the unit is changed
    from BTC/kvB to sat/vB. This is a breaking change, but it should not be an
    overly risky one, as the units change by a factor of 1e5 and any fees specified
    in BTC/kvB after this commit will either be too low and raise an error or be 1
    sat/vB and can be RBFed.
    
    Update the test coverage for each RPC.
    
    Co-authored-by: Murch <murch@murch.one>
    a0d4957473
  63. wallet: remove redundant bumpfee fee_rate checks
    SetFeeEstimateMode() handles these checks now and provides a more actionable
    error message.
    410e471fa4
  64. wallet: add fee_rate unit warnings to bumpfee b7994c01e9
  65. wallet: remove fee rates from conf_target helps 7f9835a05a
  66. jonatack commented at 3:01 pm on November 11, 2020: member

    Rebased on master after merge of #20368 to avoid the valgrind fuzz CI job time-outs.

    git diff 5660dd3 bcb8e0d still works for seeing the changes in the last update.

    The remaining open question is #20305 (review) -> Edit: done in 05e82d86b09d914ebce05dbc92a7299cb026847b, this should hopefully be ready for final review.

  67. jonatack force-pushed on Nov 11, 2020
  68. jonatack force-pushed on Nov 12, 2020
  69. wallet: update fee rate units, use sat/vB for fee_rate error messages
    and BTC/kvB for feeRate error messages.
    173b5b5fe0
  70. wallet: update remaining rpcwallet fee rate units to BTC/kvB 6da3afbaee
  71. wallet: provide valid values if invalid estimate mode passed
    Co-authored-by: Murch <murch@murch.one>
    449b730579
  72. wallet: use MIN_RELAY_TX_FEE in bumpfee help
    Co-authored-by: Murch <murch@murch.one>
    be481b72e2
  73. wallet: update sendtoaddress, send RPC examples with fee_rate 9a670b4f07
  74. wallet: override minfee checks (fOverrideFeeRate) for fee_rate
    in RPCs fundrawtransaction and walletcreatefundedpsbt only.
    
    This provides the existing feeRate (BTC/kvB) behavior in these two RPCs to the
    new fee_rate (sat/vB) param also.
    
    See these two GitHub review discussions for more info:
    https://github.com/bitcoin/bitcoin/pull/10706/#discussion_r126560525
    https://github.com/bitcoin/bitcoin/pull/20305#discussion_r520032533
    05e82d86b0
  75. jonatack force-pushed on Nov 12, 2020
  76. achow101 commented at 5:09 pm on November 12, 2020: member
    re-ACK 05e82d8
  77. Sjors approved
  78. Sjors commented at 8:32 pm on November 12, 2020: member

    Concept ACK

    utACK 05e82d86b09d914ebce05dbc92a7299cb026847b

    Concept meh on adding the v (BTC/kvB instead of BTC/kB, sat/vB instead of sat/B). The distinction between bytes and “virtual” bytes has no practical use. It’s only interesting for people who want to understand the internals of Bitcoin or why spending from a SegWit is address is cheaper.

    I found the overloaded estimate_mode param more intuitive as a bitcoin-cli user, i.e. send ... 1 sat/b before vs. send ... 0 "" 1 now. But the example in the help explains how to use it. Since the send RPC is experimental, we can overhaul it in a future version anyway.

    I agree that the bumpfee unit change seems safe, even when users downgrade their node, as explained in the commit message of a0d495747320c79b27a83c216dcc526ac8df8f24, except if you downgrade to a version before #16257 (i.e. before v0.19, and the fix has also been backported).

    Do we also want to do something about the unset estimate mode? Not sure what it does. It seems safest to make economical the default estimate mode. But this PR already has a very large scope.

  79. jonatack commented at 8:51 pm on November 12, 2020: member

    Concept meh on adding the v (BTC/kvB instead of BTC/kB, sat/vB instead of sat/B). The distinction between bytes and “virtual” bytes has no practical use. It’s only interesting for people who want to understand the internals of Bitcoin or why spending from a SegWit is address is cheaper.

    I understand and don’t have a strong opinion on sat/B vs sat/vB, happy to follow review consensus.

  80. MarcoFalke commented at 5:48 am on November 13, 2020: member

    Concept ACK 05e82d86b0

    I conceptually agree with the changes, but I haven’t reviewed them

  81. Xekyo commented at 10:29 pm on November 13, 2020: member

    Concept meh on adding the v (BTC/kvB instead of BTC/kB, sat/vB instead of sat/B). The distinction between bytes and “virtual” bytes has no practical use. It’s only interesting for people who want to understand the internals of Bitcoin or why spending from a SegWit is address is cheaper.

    I dissent: a bunch of blockexplorers still show “feerate” in sat/B, but it often refers to “fee per byte length” of the transaction (ignoring weight), some really outdated blockexplorers have until recently shown fee per stripped size under that label. In my experience, some users were confused by that, and tried to e.g. CPFP a transaction to an amount slightly higher than the “fee per byte length” which happened to still be below the actual feerate. Explicitly stating the feerate with the actually relevant measure potentially resolves that sort of confusion. Maybe that would even be a good argument to fully switch to satoshi/weightunit, but that would be a bigger change than we have scoped here at the moment and would bring another slew of UX considerations.

  82. Xekyo commented at 11:46 pm on November 13, 2020: member
    tACK 05e82d86b09d914ebce05dbc92a7299cb026847b
  83. Sjors commented at 2:08 pm on November 14, 2020: member
    If there’s still block explorers (that people use) using real bytes instead of virtual bytes in their fee calculation, then indeed adding the v might be necessary. Either way, don’t let it hold back this PR.
  84. Xekyo commented at 2:41 pm on November 16, 2020: member

    Just to provide two examples:

    This first is from Blockchair providing the fee per raw size as [sat/B] next to the actual feerate: image

    This second is Blockchain.com, providing fee per raw size as [sat/B] next to actual [sat/WU]: image

  85. luke-jr commented at 8:02 pm on November 16, 2020: member
    Block explorer websites have been confusing users with bogus “information” forever. Trying to match them doesn’t make sense.
  86. MarcoFalke commented at 12:48 pm on November 17, 2020: member

    review ACK 05e82d86b0 did not test and found a few style nits, which can be fixed later 🍯

    Signature:

     0-----BEGIN PGP SIGNED MESSAGE-----
     1Hash: SHA512
     2
     3review ACK 05e82d86b0 did not test and found a few style nits, which can be fixed later 🍯
     4-----BEGIN PGP SIGNATURE-----
     5
     6iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
     7pUi04QwAtjdQi2xSn12WwCON4MXuHDxB7owu5U1QxHMD5UpoaASOromS45wnAaaS
     8X2VhwFoYWJWkPAeCycZ0yr+tyhjr91Z8JqcPpPheryzIKyVys1QMSVVU4Nc4m/2D
     9PrQOhzn5BX74CnYU+lNoqRECzKslJGXypjWnJz8zmyGU41IDbl7LxQf5sOwYw0kD
    10k0LDLAcQyg1jLhAwlIvhFMccvzUGz49eXxRfpSO/8qghRm4thKyLvQ7l3nN+zGM7
    11lQuYdo3MU+OOi82fHJAnsLVwziDVxe0hVr1/r4OHaaeRMJsFSzPVRVox/JV7Zq1E
    12B8OG5GtifbGR1TqTYhUFZaNCVLKiFCmiKn2SY1VR6UoGonHGgtz/JnQzmpSJbqAd
    135i4HtojlkPgtMlLuEadZWmwVNNcpJzgpWjubbc0489/5rrhBg9E+tSSQ0KvclzTg
    14ZBAEKdSJWy/jGvyxZri7Rzp+Z8c13cLo/oWKw98Y6PV12+uNNv2M+DV/jdQkEasW
    15GaGTrQnL
    16=h9+z
    17-----END PGP SIGNATURE-----
    

    python: error while loading shared libraries: libpython3.8.so.1.0: cannot open shared object file: No such file or directory

  87. MarcoFalke merged this on Nov 17, 2020
  88. MarcoFalke closed this on Nov 17, 2020

  89. Sjors commented at 12:56 pm on November 17, 2020: member
    I contacted one such explorer and nagged them to switch from bytes to vbytes :-)
  90. jonatack deleted the branch on Nov 17, 2020
  91. sidhujag referenced this in commit 8518ab9051 on Nov 17, 2020
  92. MarcoFalke added the label Needs release note on Nov 17, 2020
  93. MarcoFalke commented at 6:38 pm on November 17, 2020: member
    This probably needs release notes?
  94. MarcoFalke commented at 6:50 pm on November 17, 2020: member
    Btw, the first commit adds tests that pass without the code changes. I haven’t tried the other added tests, but as a general rule, I’d prefer if new tests were added in a separate commit and not in the same commit that also changes behaviour.
  95. in src/wallet/rpcwallet.cpp:3395 in 05e82d86b0
    3391@@ -3373,35 +3392,38 @@ RPCHelpMan signrawtransactionwithwallet()
    3392 static RPCHelpMan bumpfee_helper(std::string method_name)
    3393 {
    3394     bool want_psbt = method_name == "psbtbumpfee";
    3395+    const std::string incremental_fee{CFeeRate(DEFAULT_MIN_RELAY_TX_FEE).ToString(FeeEstimateMode::SAT_VB)};
    


    MarcoFalke commented at 6:51 pm on November 17, 2020:
    DEFAULT_INCREMENTAL_RELAY_FEE, no?

    jonatack commented at 7:13 pm on November 17, 2020:
    Agreed, this looks like an oversight.

    jonatack commented at 7:18 pm on November 17, 2020:
    Note, remove #include <validation.h> in rpcwallet.cpp as part of the change.

    jonatack commented at 6:23 pm on November 19, 2020:
    Done in #20426, thanks for the catch @MarcoFalke
  96. jonatack commented at 6:53 pm on November 17, 2020: member

    This probably needs release notes?

    Yes, will update the wiki.

  97. MarcoFalke commented at 6:55 pm on November 17, 2020: member
    Turns out the style nits are regressions. See https://github.com/bitcoin/bitcoin/pull/20305/files#r525406176 and #20410
  98. MONIMAKER365 commented at 0:33 am on November 22, 2020: none
  99. jonatack commented at 4:48 pm on November 26, 2020: member

    Release notes added to the wiki at https://github.com/bitcoin-core/bitcoin-devwiki/wiki/0.21.0-Release-Notes-Draft.

    The Needs release note tag can be removed.

  100. fanquake removed the label Needs release note on Nov 26, 2020
  101. janus referenced this in commit 2a92a5fba8 on Dec 13, 2020
  102. MarcoFalke locked this on Feb 15, 2022

github-metadata-mirror

This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2024-07-01 10:13 UTC

This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me