sendtoaddress
/sendmany
if they prefer this over the estimators.
sendtoaddress
/sendmany
if they prefer this over the estimators.
Not opposed to add that. Though IMO the more experience users (which are those who may use explicit fee-rates) should use create/fund/sendrawtx.
Concept ACK
53@@ -54,6 +54,7 @@ bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_esti
54 {"UNSET", FeeEstimateMode::UNSET},
55 {"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
56 {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
57+ {"EXPLICIT", FeeEstimateMode::EXPLICIT},
{"NONE", FeeEstimateMode::NONE}
?
444@@ -445,12 +445,15 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
445 " \"UNSET\"\n"
446 " \"ECONOMICAL\"\n"
447 " \"CONSERVATIVE\"\n"
448+ " \"EXPLICIT\"\n"
449+ "9. feerate (numeric, optional) Fee rate (sat/kb) to use for EXPLICIT mode.\n"
estimate_mode
must be set to (currenlty) EXPLICIT
to use this value?
to use for EXPLICIT mode
is not enough?
496@@ -494,6 +497,12 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
497 }
498 }
499
500+ if (!request.params[8].isNull()) {
501+ if (coin_control.m_fee_mode != FeeEstimateMode::EXPLICIT) {
502+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Explicit fee rate can only be used when estimate mode is set to EXPLICIT");
Concept ACK. I don’t like that this results in sendtoaddress having 8 parameters but I don’t see an immediately obvious way to avoid that.
Sendmany could get it too?
settxfee
.
bumpfee
, listunspent
, etc. use since most are independent of each other and optional
38@@ -39,6 +39,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
39 { "sendtoaddress", 4, "subtractfeefromamount" },
40 { "sendtoaddress", 5 , "replaceable" },
41 { "sendtoaddress", 6 , "conf_target" },
42+ { "sendtoaddress", 8, "feerate"},
"feerate"
sendmany
should get it too.
444@@ -445,12 +445,15 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
445 " \"UNSET\"\n"
446 " \"ECONOMICAL\"\n"
447 " \"CONSERVATIVE\"\n"
448+ " \"EXPLICIT\"\n"
449+ "9. feerate (numeric, optional) Fee rate (sat/kb) to use for EXPLICIT mode. (Note: replaceable defaults to true when used)\n"
496@@ -494,6 +497,14 @@ UniValue sendtoaddress(const JSONRPCRequest& request)
497 }
498 }
499
500+ if (!request.params[8].isNull()) {
params[6]
be conf target or feerate…
419+ if (coin_control.m_fee_mode != FeeEstimateMode::EXPLICIT) {
420+ coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
421+ } else {
422+ coin_control.m_feerate = CFeeRate(request.params[6].get_int());
423+ // default RBF to true for explicit fee rate mode
424+ coin_control.m_signal_bip125_rbf = (coin_control.m_signal_bip125_rbf ? *coin_control.m_signal_bip125_rbf : false) | request.params[5].isNull();
02018-09-19 13:23:27 clang-tidy(pr=11413): src/wallet/rpcwallet.cpp:420:48: warning: implicit conversion bool -> 'int' [readability-implicit-bool-conversion]
12018-09-19 13:23:27 clang-tidy(pr=11413): src/wallet/rpcwallet.cpp:420:129: warning: implicit conversion bool -> 'int' [readability-implicit-bool-conversion]
|
?
856+ if (coin_control.m_fee_mode != FeeEstimateMode::EXPLICIT) {
857+ coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
858+ } else {
859+ coin_control.m_feerate = CFeeRate(request.params[6].get_int());
860+ // default RBF to true for explicit fee rate mode
861+ coin_control.m_signal_bip125_rbf = (coin_control.m_signal_bip125_rbf ? *coin_control.m_signal_bip125_rbf : false) | request.params[5].isNull();
02018-09-19 13:23:27 clang-tidy(pr=11413): src/wallet/rpcwallet.cpp:857:48: warning: implicit conversion bool -> 'int' [readability-implicit-bool-conversion]
12018-09-19 13:23:27 clang-tidy(pr=11413): src/wallet/rpcwallet.cpp:857:129: warning: implicit conversion bool -> 'int' [readability-implicit-bool-conversion]
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.
Reviewers, this pull request conflicts with the following ones:
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.
354@@ -355,17 +355,20 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
355 "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n"
356 " The recipient will receive less bitcoins than you enter in the amount field.\n"
357 "6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n"
358- "7. conf_target (numeric, optional) Confirmation target (in blocks)\n"
359+ " Defaults to false unless explicit estimate mode is used, in which case it defaults to true\n"
360+ "7. conf_target (numeric, optional) Confirmation target (in blocks), or fee rate (sat/kb) if explicit estimate mode is used\n"
0@@ -0,0 +1,5 @@
1+Low-level RPC changes
doc/release-notes-14454.md
?
356@@ -357,17 +357,20 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
357 "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n"
358 " The recipient will receive less bitcoins than you enter in the amount field.\n"
359 "6. replaceable (boolean, optional) Allow this transaction to be replaced by a transaction with higher fees via BIP 125\n"
360- "7. conf_target (numeric, optional) Confirmation target (in blocks)\n"
361+ " Defaults to false unless explicit estimate mode is used, in which case it defaults to true\n"
362+ "7. conf_target (numeric, optional) Confirmation target (in blocks), or fee rate (BTC/kb) if explicit estimate mode is used\n"
CURRENCY_UNIT
here.
377@@ -377,6 +378,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
378 + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
379 + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"")
380 + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true")
381+ + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" false true 2000 EXPLICIT")
21@@ -22,6 +22,8 @@ class WalletTest(BitcoinTestFramework):
22 def set_test_params(self):
23 self.num_nodes = 4
24 self.setup_clean_chain = True
25+ # node 2 needs a txindex to look up raw transactions
26+ self.extra_args = [[], [], ["-txindex"], []]
This is part of the wrong commit. The sendtoaddress
test only calls getrawtransaction
on a transaction in the mempool, which doesn’t need a tx index.
Also, the sendmany
test that relies on this could just pass the block hash explicitly to avoid building the tx index.
getrawtransaction
from sendmany, so this seems completely unnecessary?
363+ conf_target=0.00002500,
364+ estimate_mode='EXPLICIT',
365+ )
366+ tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])
367+ self.sync_all([self.nodes[0:3]])
368+ self.nodes[0].generate(1)
sync_all
here or else there’s a race condition.
Done. Also squashed things.
For shitcoiners, this is a drawback as a new hard-coded coin name (“btc” in “btc/kb”) is introduced, but don’t think anyone cares about that.
30@@ -31,6 +31,7 @@ bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_esti
31 {"UNSET", FeeEstimateMode::UNSET},
32 {"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
33 {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
34+ {"btc/kb", FeeEstimateMode::BTC_KB},
I think we use upper case. See also
0src/policy/feerate.cpp:const std::string CURRENCY_UNIT = "BTC";
which you can use to express your love of shitcoins.
+1 for case insensitive
Also fine with CURRENCY_UNIT
I don’t care about the kB
vs kb
distinction as far as the parser goes, because nobody uses bits in the context of fees, but documentation wise we should do it correctly. Users will out by themselves it’s case insensitive :-)
49@@ -50,6 +50,8 @@ enum class FeeEstimateMode {
50 UNSET, //!< Use default settings based on other criteria
51 ECONOMICAL, //!< Force estimateSmartFee to use non-conservative estimates
52 CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
53+ BTC_KB, //!< Use explicit BTC/kB fee given in coin control
54+ SAT_B, //!< Use explicit SAT/b fee given in coin control
375@@ -360,11 +376,13 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
376 {"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
377 " The recipient will receive less bitcoins than you enter in the amount field."},
378 {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
379- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
380- {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
381+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (" + CURRENCY_UNIT + "/kB) if " + (CURRENCY_UNIT + "/kB") + " estimate mode is used"},
836@@ -816,11 +837,13 @@ static UniValue sendmany(const JSONRPCRequest& request)
837 },
838 },
839 {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
840- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
841- {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
842+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (" + CURRENCY_UNIT + "/kB) if " + (CURRENCY_UNIT + "/kB") + " estimate mode is used"},
A separate PR for the uppercase/lowercase makes sense. It looks simple enough, but it does touch networking code.
utACK 626d26c modulo documentation bug.
387@@ -388,7 +388,7 @@ bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::strin
388 key.erase(is_index);
389 }
390 #ifdef WIN32
391- std::transform(key.begin(), key.end(), key.begin(), ToLower);
49@@ -50,6 +50,8 @@ enum class FeeEstimateMode {
50 UNSET, //!< Use default settings based on other criteria
51 ECONOMICAL, //!< Force estimateSmartFee to use non-conservative estimates
52 CONSERVATIVE, //!< Force estimateSmartFee to use conservative estimates
53+ BTC_KB, //!< Use explicit BTC/kB fee given in coin control
54+ SAT_B, //!< Use explicit SAT/B fee given in coin control
FeeEstimateMode
. With your suggestion that wouldn’t be sufficient.
432@@ -413,10 +433,6 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
433 coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
434 }
435
436- if (!request.params[6].isNull()) {
437- coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
438- }
439-
440 if (!request.params[7].isNull()) {
441 if (!FeeModeFromString(request.params[7].get_str(), coin_control.m_fee_mode)) {
SetFeeEstimateParam
…?
375@@ -360,11 +376,13 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
376 {"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
377 " The recipient will receive less bitcoins than you enter in the amount field."},
378 {"replaceable", RPCArg::Type::BOOL, /* default */ "wallet default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
379- {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
380- {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
381+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/KB or " + CURRENCY_ATOM + "/B estimate modes)"},
7@@ -8,6 +8,7 @@
8 #include <tinyformat.h>
9
10 const std::string CURRENCY_UNIT = "BTC";
11+const std::string CURRENCY_ATOM = "SAT";
ToLower(CURRENCY_ATOM)
in future uses. Or alternatively I use ToUpper(CURRENCY_ATOM)
here…
ToUpper(CURRENCY_ATOM)
makes more sense, since it could just as well be “cXBT” or such in some hypothetical sidechain or future hardfork.
ToUpper
was necessary: 4b304fefc517404bb109041cfcfa2a2c576c9a42
FeeModeFromString
is actually necessary.
392@@ -375,6 +393,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
393 HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1")
394 + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"")
395 + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true")
396+ + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" false true 0.00002 " + (CURRENCY_UNIT + "/kB"))
397+ + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" false true 2 " + (CURRENCY_ATOM + "/b"))
27@@ -26,15 +28,31 @@ std::string StringForFeeReason(FeeReason reason) {
28 return reason_string->second;
29 }
30
31-bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
32+const std::map<std::string, FeeEstimateMode>& FeeModeMap() {
42+}
43+
44+std::string FeeModes(const std::string& delimiter = ", ") {
45+ std::ostringstream r;
46+ bool first = true;
47+ for (const auto& i : FeeModeMap()) {
3049- }
3050- coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"], pwallet->chain().estimateMaxBlocks());
3051- }
3052- if (options.exists("estimate_mode")) {
3053- if (options.exists("feeRate")) {
3054- throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both estimate_mode and feeRate");
3329@@ -3329,10 +3330,11 @@ static UniValue bumpfee(const JSONRPCRequest& request)
3330 },
3331 true, true);
3332
3333- if (options.exists("confTarget") && options.exists("totalFee")) {
3334+ auto conf_target_exists = options.exists("confTarget") || options.exists("conf_target");
!conf_target.IsNull()
Split into 6 commits:
26@@ -26,15 +27,29 @@ std::string StringForFeeReason(FeeReason reason) {
27 return reason_string->second;
28 }
29
30-bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
31+const std::map<std::string, FeeEstimateMode>& FeeModeMap() {
32 static const std::map<std::string, FeeEstimateMode> fee_modes = {
<std::vector<std::pair<std::string, FeeEstimateMode>>
so you can control the display order?
174+ if (cc.m_fee_mode == FeeEstimateMode::SAT_B) {
175+ fee_rate /= 100000; // 1 sat / B = 0.00001 BTC / kB
176+ }
177+ cc.m_feerate = CFeeRate(fee_rate);
178+ // default RBF to true for explicit fee rate mode
179+ cc.m_signal_bip125_rbf = (cc.m_signal_bip125_rbf ? *cc.m_signal_bip125_rbf : false) || may_set_rbf;
may_set_rbf
and use if(cc.m_signal_bip125_rbf) {*cc.m_signal_bip125_rbf = true}
. See 4594923046585453beef83e833e6a422a78b66b2
if (!cc.m_signal_bip125_rbf)
? I thought optionals returned 0
when unset, and we only wanna set it if it’s not been set already, right?
Wading through the slugfest that is boost, it seems that the most correct way to determine if an optional is uninitialized is to do == boost::none
. I.e.
0if (cc.m_signal_bip125_rbf == boost::none) { ... }
boost::none
384- {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", "The fee estimate mode, must be one of:\n"
385- " \"UNSET\"\n"
386- " \"ECONOMICAL\"\n"
387- " \"CONSERVATIVE\""},
388+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks), or fee rate (for " + CURRENCY_UNIT + "/KB or " + ToUpper(CURRENCY_ATOM) + "/B estimate modes)"},
389+ {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", std::string() + "The fee estimate mode, must be one of (case insensitive):\n"
" (case insensitive)"
addition is part of the wrong commit.
874b40cea1644812a416a517c3a4a3ab1db89144
What’s the motivation to support case insensitive?
43- auto mode = fee_modes.find(mode_string);
44+ return fee_modes;
45+}
46
47- if (mode == fee_modes.end()) return false;
48+std::string FeeModes(const std::string& delimiter = ", ") {
FeeMode
- probably should be FeeModes
, so fix commit message
A default ", "
seemed like a sensible thing, but you’re right that I don’t use the default anywhere.
Also, huh. I thought re-declaring the defaults in the .cpp file would result in a compiler error. Regardless, removed both.
3322@@ -3323,16 +3323,20 @@ static UniValue bumpfee(const JSONRPCRequest& request)
3323 RPCTypeCheckObj(options,
3324 {
3325 {"confTarget", UniValueType(UniValue::VNUM)},
3326+ {"conf_target", UniValueType(UniValue::VNUM)},
confTarget
is given? Doesn’t make any sense to me and seems completely unrelated to this pull request anyway.
confTarget
. But at least there should be a throw if both are set. You could also rename the documented options to conf_target
, but keep confTarget
as an undocumented alias for backwards compatibility. That should be in the release note though.
0@@ -0,0 +1,8 @@
1+Low-level RPC changes
816- " \"UNSET\"\n"
817- " \"ECONOMICAL\"\n"
818- " \"CONSERVATIVE\""},
819+ {"estimate_mode", RPCArg::Type::STR, /* default */ "UNSET", std::string() + "The fee estimate mode, must be one of:\n"
820+ " \"" + FeeModes("\"\n"
821+ " \"") + "\""},
29@@ -28,8 +30,8 @@ std::string StringForFeeReason(FeeReason reason) {
30 return reason_string->second;
31 }
32
33-const std::map<std::string, FeeEstimateMode>& FeeModeMap() {
34- static const std::map<std::string, FeeEstimateMode> fee_modes = {
35+const std::vector<std::pair<std::string, FeeEstimateMode>>& FeeModeMap() {
36+ static const std::vector<std::pair<std::string, FeeEstimateMode>> fee_modes = {
157@@ -158,6 +158,30 @@ static std::string LabelFromValue(const UniValue& value)
158 return label;
159 }
160
161+static void SetFeeEstimateMode(CWallet* const pwallet, CCoinControl& cc, const UniValue& estimate_mode, const UniValue& estimate_param)
const CWallet& wallet
instead? I don’t see that used very often anywhere, and it seems this thing is usually a std::shared_ptr
.
3329 {"estimate_mode", UniValueType(UniValue::VSTR)},
3330 },
3331 true, true);
3332
3333- if (options.exists("confTarget") && options.exists("totalFee")) {
3334- throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and totalFee options should not both be set. Please provide either a confirmation target for fee estimation or an explicit total fee for the transaction.");
1c0d97daa09f2fc792b8675ea535ba03c8a7b86f
There was no test for this 😞 And the new errors don’t have tests either. Care to add?
53+}
54
55- fee_estimate_mode = mode->second;
56- return true;
57+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
58+ auto vector = FeeModeMap();
457396a4091bb3c27b1e982fb7e08442120b15df
Why?
: FeeModeMap()
directly? Good point.
28@@ -26,16 +29,32 @@ std::string StringForFeeReason(FeeReason reason) {
29 return reason_string->second;
30 }
31
32-bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
33- static const std::map<std::string, FeeEstimateMode> fee_modes = {
34+const std::vector<std::pair<std::string, FeeEstimateMode>>& FeeModeMap() {
457396a4091bb3c27b1e982fb7e08442120b15df
Why have this as a function? Could be a static variable in this translation unit, like
0static const std::vector<std::pair<std::string, FeeEstimateMode>> FEE_MODES{
1 {"UNSET", FeeEstimateMode::UNSET},
2 {"ECONOMICAL", FeeEstimateMode::ECONOMICAL},
3 {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
4 {(CURRENCY_UNIT + "/KB"), FeeEstimateMode::BTC_KB},
5 {(ToUpper(CURRENCY_ATOM) + "/B"), FeeEstimateMode::SAT_B},
6};
ToUpper
is no longer used, this may actually work in Windows; tentatively removing the function wrapper.
41+ return fee_modes;
42+}
43
44- if (mode == fee_modes.end()) return false;
45+std::string FeeModes(const std::string& delimiter) {
46+ std::ostringstream r;
457396a4091bb3c27b1e982fb7e08442120b15df
Is it really necessary to #include <sstream>
just to concat a couple of strings? Could just do
0for (i : ...) {
1 if (!res.empty()) res += delimiter;
2 res += i.first
3}
I don’t really see the harm in including it (it won’t make the binary bigger or anything) but it’s such a trivial set of operations that I doubt it’ll matter in the long run.
I.e. removing the include and just stacking onto an std::string
.
40- auto mode = fee_modes.find(mode_string);
41+ return fee_modes;
42+}
43
44- if (mode == fee_modes.end()) return false;
45+std::string FeeModes(const std::string& delimiter) {
457396a4091bb3c27b1e982fb7e08442120b15df
This is a utility function for the RPC help, I’d move to rpcwallet.cpp
and make it static there.
BTW, according to dev notes
Braces on new lines for classes, functions, methods.
FeeModes
, but it would mean having to make fee_modes
available to the public, which doesn’t seem worth it.
59- return true;
60+bool FeeModeFromString(const std::string& mode_string, FeeEstimateMode& fee_estimate_mode) {
61+ auto searchkey = ToUpper(mode_string);
62+ auto vector = FeeModeMap();
63+ for (const auto& pair : vector) {
64+ if (pair.first == searchkey) {
Regarding https://github.com/bitcoin/bitcoin/pull/11413/files#r313929142
Then I’d do ToUpper(pair.first) == searchkey)
and display the right casing.
37- {"CONSERVATIVE", FeeEstimateMode::CONSERVATIVE},
38- };
39- auto mode = fee_modes.find(mode_string);
40-
41- if (mode == fee_modes.end()) return false;
42+static const std::vector<std::pair<std::string, FeeEstimateMode>> fee_modes = {
FEE_MODES
.
87@@ -87,6 +88,38 @@ def run_test(self):
88 test_no_more_inputs_fails(rbf_node, dest_address)
89 self.log.info("Success")
90
91+def test_invalid_parameters(node, dest_address):
92+ txid = spend_one_input(node, dest_address)
93+ # invalid estimate mode
94+ assert_raises_rpc_error(-8, "Invalid estimate_mode parameter", node.bumpfee, txid, {
:
here and below.
0@@ -0,0 +1,10 @@
1+Updated or changed RPC
2+----------------------
3+
4+The `bumpfee`, `fundrawtransaction`, `sendmany`, `sendtoaddress`, and `walletcreatefundedpsbt`
5+RPC commands have been updated to include two new fee estimation methods "BTC/KB" and "SAT/B".
5+RPC commands have been updated to include two new fee estimation methods "BTC/KB" and "SAT/B".
6+The target is the fee expressed explicitly in the given form.
7+
8+In addition, the estimate_mode parameter is now case insensitive for all of the above RPC commands.
9+
10+The `bumpfee` command now uses `conf_target` rather than `confTarget` in the options.
confTarget
is still supported but clients should transition to conf_target
.
49+
50+std::string FeeModes(const std::string& delimiter)
51+{
52+ std::string r;
53+ std::string prefix = "";
54+ for (const auto& i : fee_modes) {
const std::string
s in the .h
file. We do that elsewhere so I assume nobody will complain about moving CURRENCY_UNIT
into feerate.h
from feerate.cpp
.
conf_target
be required when using an explicit estimate mode?
177+ fee_rate /= 100000; // 1 sat / B = 0.00001 BTC / kB
178+ }
179+ cc.m_feerate = CFeeRate(fee_rate);
180+ // default RBF to true for explicit fee rate modes
181+ if (cc.m_signal_bip125_rbf == boost::none) cc.m_signal_bip125_rbf = true;
182+ } else {
else if (! estimate_param.isNull()) {
?
concept ACK, but I am really wary of the implementation of the arguments.
The meaning of conf_target
basically inverts with this new argument. Consider:
sendtoaddress 2N7GTLzhS9n3FzwAQohAgnk2kGvt8eE9nng 1 "" "" false true 1008
and
sendtoaddress 2N7GTLzhS9n3FzwAQohAgnk2kGvt8eE9nng 1 "" "" false true 1008 sat/B
and the opposite way:
1
vs 1 sat/B
is likely considerable.
I’d rather we “overload” the conf_target
and parse 30sat/B
directly, disallowing estimate mode to be specified maybe other than unset
.
416+ self.nodes[0].generate(1)
417+ self.sync_all(self.nodes[0:3])
418+ postbalance = self.nodes[2].getbalance()
419+ fee = prebalance - postbalance - Decimal('1')
420+ assert_fee_amount(fee, tx_size, Decimal('0.00002000'))
421+
I’d rather we “overload” the conf_target and parse 30sat/B directly, disallowing estimate mode to be specified maybe other than unset.
If that’s easy to implement I also think it’s significantly more clear.
1
and forgets to write sat/B
. I’m not sure if there’s a backwards compatible way to solve this.
Specifying an amount as a string like that is completely foreign to all existing RPC interfaces.
The number is already interpreted differently based on the mode string.
@instagibbs Yeah, typing ‘1’ would mean ‘confirm within 1 block’ which is directly opposite of ‘1 sat/b’.
Adding number suffixes only partially solves the problem. Someone who accidentally hits enter forgetting the ‘sat/b’ suffix will have exactly the same issue.
Perhaps the explicit modes should not overload conf_target
but instead use a new argument entirely. Alternatively we make the estimate_mode include the actual number, so people will do
0[...]conf_target=null estimate_mode=1sat/b [...]
which means the ambiguity is removed. If they do estimate_mode=1
it will error. And conf_target
no longer both means “low value = high priority = potentially high fees” and “low value = low fees” at the same time, depending on which estimate mode is used at the time.
I don’t like including the string in the same param (without a space). Nor do I like the verboseness of named params.
Making a breaking change might be worth it for the extra safety. It would fail quite gracefully, i.e. it just stops sending. The impact can be reduced by not requiring estimate_mode
when using named parameters.
193+ if (cc.m_fee_mode == FeeEstimateMode::SAT_B) {
194+ fee_rate /= 100000; // 1 sat / B = 0.00001 BTC / kB
195+ }
196+ cc.m_feerate = CFeeRate(fee_rate);
197+ // if feerate < 0.1 sat/b, throw
198+ if (fee_rate < 100) {
38@@ -39,6 +39,10 @@
39
40 static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
41
42+static const uint32_t WALLET_MIN_FEERATE = 100; // sat/b
43+
44+static const uint32_t WALLET_BTC_KB_TO_SAT_B = 100000; // 1 sat / B = 0.00001 BTC / kB
COIN
const for this…
COIN / 1000
.
38@@ -39,6 +39,10 @@
39
40 static const std::string WALLET_ENDPOINT_BASE = "/wallet/";
41
42+static const uint32_t WALLET_MIN_FEERATE = 100; // sat/b
DEFAULT_MIN_RELAY_TX_FEE
instead.
validation.h
to access that, and I can’t access it via extern
because it’s static
. I’m just doing 1000 /* DEFAULT_MIN_RELAY_TX_FEE */
for now.
Tested ACK 398be1c modulo squash.
Positional arguments are nigh-impossible to use, but let’s worry about that later…
0bitcoin-cli sendtoaddress bc... 1 "" "" false true 1 sat/b
1bitcoin-cli sendmany "" '{"bc...": 1}' 0 "" '[]' true 1 sat/b
A mix of positional and named kind of works:
0sendtoaddress bcrt1qgr3zfmvqzl4uv3utz6h849x0fu5a8mvg695jde 1 -conf_target=1 estimate_mode=sat/b
390@@ -338,6 +391,74 @@ def run_test(self):
391 # This will raise an exception for importing an invalid pubkey
392 assert_raises_rpc_error(-5, "Pubkey is not a valid public key", self.nodes[0].importpubkey, "5361746f736869204e616b616d6f746f")
393
394+ # send with explicit btc/kb fee
395+ self.sync_all(self.nodes[0:3])
396+ self.log.info("test explicit fee (sendtoaddress as btc/kb)")
397+ self.nodes[0].generate(1)
sync_all
needs to be moved after generate
, or prebalance
could end up with a stale balance and cause the check below (line 426) to fail.
3340@@ -3341,7 +3341,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
3341 {"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The txid to be bumped"},
3342 {"options", RPCArg::Type::OBJ, RPCArg::Optional::OMITTED_NAMED_ARG, "",
3343 {
3344- {"confTarget", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
3345+ {"conf_target", RPCArg::Type::NUM, /* default */ "wallet default", "Confirmation target (in blocks)"},
193+ }
194+ }
195+
196+ if (cc.m_fee_mode == FeeEstimateMode::BTC_KB || cc.m_fee_mode == FeeEstimateMode::SAT_B) {
197+ if (estimate_param.isNull()) {
198+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Selected estimate_mode requires a confirmation target");
s/confirmation target/fee rate/
?
184@@ -183,6 +185,33 @@ static std::string LabelFromValue(const UniValue& value)
185 return label;
186 }
187
188+static void SetFeeEstimateMode(CWallet* const pwallet, CCoinControl& cc, const UniValue& estimate_mode, const UniValue& estimate_param)
0@@ -0,0 +1,10 @@
1+Updated or changed RPC
2+----------------------
3+
4+The `bumpfee`, `fundrawtransaction`, `sendmany`, `sendtoaddress`, and `walletcreatefundedpsbt`
~Code review re-ACK 74f9d2797bd199fcd4cfc30e490bbac6224424f9~
Actually there’s a problem the user tries to set the fee rate to 0.1 sat/b
. It silently rounds up to 1
. This should throw an error or work (setting the fee slightly below the min relay fee, e.g. 1.2 sat/b can be useful).
To reproduce, add this to wallet_basic_py
0 txid = self.nodes[2].sendtoaddress(
1 address=address,
2 amount=1.0,
3 conf_target=0.2,
4 estimate_mode='SAT/B',
5 )
6 tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])
7 self.sync_all(self.nodes[0:3])
8 self.nodes[0].generate(1)
9 self.sync_all(self.nodes[0:3])
10 postbalance = self.nodes[2].getbalance()
11 fee = prebalance - postbalance - Decimal('1')
12 assert_fee_amount(fee, tx_size, Decimal('0.00000200'))
Result:
0 File "test/functional/wallet_basic.py", line 476, in run_test
1 assert_fee_amount(fee, tx_size, Decimal('0.00000200'))
2 File "/Users/sjors/dev/bitcoin/test/functional/test_framework/util.py", line 42, in assert_fee_amount
3 raise AssertionError("Fee of %s BTC too high! (Should be %s BTC)" % (str(fee), str(target_fee)))
4AssertionError: Fee of 1.00000423 BTC too high! (Should be 2.8E-7 BTC)
Updated code to now return an error when creating a transaction if the requested explicit fee rate differs from the required fee rate. This prevents silently ignoring the user when they want a fee rate < ~1 sat/b~ the current minimum fee rate (which may change due to mempool state, and/or future changes to the current 1 sat/b minimum policy default) – it now gives an error, as you would expect.
Edit: changed from “1 sat/b” to a more accurate description.
2659@@ -2660,6 +2660,12 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
2660
2661 // Get the fee rate to use effective values in coin selection
2662 CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
2663+ // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
2664+ // provided one
2665+ if (coin_control.m_feerate && nFeeRateNeeded != *coin_control.m_feerate) {
2666+ strFailReason = "Fee rate needed differs from explicitly provided fee rate";
Fee rate is too low for the current mempool. Increase it to %f sat/b or modify -maxmempool
That’s a bit weird, though, for the case when the relay fee is at the minimum. We can’t really ask people to change minrelaytxfee
either, as that would mean their node ends up with non-relayable (to most other nodes) transactions. I don’t really see a better alternative though, so I’m going with your version with some tweaks:
I built on this a tiny bit, adding a new optional FeeEstimateMode
parameter to CFeeRate::ToString()
, which returns sat/b formatted fee rate if estimate mode is sat/b, otherwise the (pre-existing) btc/kb fee rate. So if a user provides an explicit fee rate using sat/b
, they get the needed one back in sat/b, otherwise it is expressed in btc/kb.
IIUC coin selection uses nFeeRateNeeded
here:
0coin_selection_params.effective_fee = nFeeRateNeeded;
This doesn’t make sense for transaction with a future nLockTime, and with PSBTs it’s debatable, since we don’t know when the user intends to broadcast. For sendmany
and sendtoaddress
the current behavior is fine, because those RPCs don’t support nLockTime and PSBT.
For PSBT I’m inclined to think that effective_fee
should be set to the manual fee. In #16378 I plan to do the same for the new send
RPC. It returns the hex in that case (as it does with nLockTime
).
So basically, you’d prefer if I could force a transaction using a fee rate that is currently not actually accepted by the mempool? I initially did that, but the result is that the tx goes into the mempool but is rejected by the node’s peers, so it is never mined (unless the node itself generates a block). That seems awfully confusing.
Perhaps there should be an exception for the locktime>now case, where a warning is produced but the tx is created since it can’t go into the mempool anyway, due to locktime not unfulfilled.
This can be achieved by setting the fOverrideFeeRate
to true
, btw.
fOverrideFeeRate
could do the trick, but it’s pretty ugly: it basically “sabotages” GetMinimumFeeRate
. It seems more readable to skip GetMinimumFeeRate
altogether when you want to ignore it.
34@@ -37,7 +35,10 @@ CAmount CFeeRate::GetFee(size_t nBytes_) const
35 return nFee;
36 }
37
38-std::string CFeeRate::ToString() const
39+std::string CFeeRate::ToString(const FeeEstimateMode& fee_estimate_mode) const
40 {
41- return strprintf("%d.%08d %s/kB", nSatoshisPerK / COIN, nSatoshisPerK % COIN, CURRENCY_UNIT);
42+ switch (fee_estimate_mode) {
43+ case FeeEstimateMode::SAT_B: return strprintf("%d.%03d %s/b", nSatoshisPerK / 1000, nSatoshisPerK % 1000, CURRENCY_ATOM);
2582@@ -2583,6 +2583,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
2583 int& nChangePosInOut, std::string& strFailReason, const CCoinControl& coin_control, bool sign)
2584 {
2585 CAmount nValue = 0;
2586+ nFeeRet = 0;
Probably. I had a garbage value being returned and it ended up telling me “you need at least 4587368374.13587638 bitcoin in fees!” here:
2659@@ -2660,6 +2660,12 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
2660
2661 // Get the fee rate to use effective values in coin selection
2662 CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
2663+ // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
utACK https://github.com/bitcoin/bitcoin/pull/11413/commits/021c253961d6c51dbc1d031449b1b3d0f5edef8b
I’ll hopefully get around to testing a bit.
2659@@ -2660,6 +2660,12 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
2660
2661 // Get the fee rate to use effective values in coin selection
2662 CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
2663+ // Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
2664+ // provided one
2665+ if (coin_control.m_feerate && nFeeRateNeeded > *coin_control.m_feerate) {
2666+ strFailReason = strprintf("Fee rate is too low for the current mempool. Increase it to %s or modify -maxmempool", nFeeRateNeeded.ToString(coin_control.m_fee_mode));
-maxmempool
advice pretty dangerous for users. It’s going to simply result in stuck transactions, no?
0@@ -0,0 +1,11 @@
1+Updated or changed RPC
2+----------------------
3+
4+The `bumpfee`, `fundrawtransaction`, `sendmany`, `sendtoaddress`, and `walletcreatefundedpsbt`
5+RPC commands have been updated to include two new fee estimation methods "BTC/kB" and "sat/B".
6+The target is the fee expressed explicitly in the given form. Note that use of this feature
7+will trigger bip125 (replace-by-fee) opt-in.
481+ # 1. Send some coins to generate new UTXO
482+ address_to_import = self.nodes[2].getnewaddress()
483+ txid = self.nodes[0].sendtoaddress(address_to_import, 1)
484+ self.nodes[0].generate(1)
485+ self.sync_all(self.nodes[0:3])
486+
43+static const std::vector<std::pair<std::string, FeeEstimateMode>> FEE_MODES = {
44+ {"unset", FeeEstimateMode::UNSET},
45+ {"economical", FeeEstimateMode::ECONOMICAL},
46+ {"conservative", FeeEstimateMode::CONSERVATIVE},
47+ {(CURRENCY_UNIT + "/kB"), FeeEstimateMode::BTC_KB},
48+ {(CURRENCY_ATOM + "/B"), FeeEstimateMode::SAT_B},
CURRENCY_*
here in global scope, since the initialisation order is undefined. A simple static function wrapper should work.
also moved CURRENCY_* into feerate.h file to work around MSVC bug
53
54- if (mode == fee_modes.end()) return false;
55+std::string FeeModes(const std::string& delimiter)
56+{
57+ std::string r;
58+ std::string prefix = "";
Join()
in util/string.h
50- auto mode = fee_modes.find(mode_string);
51+ return FEE_MODES;
52+}
53
54- if (mode == fee_modes.end()) return false;
55+std::string FeeModes(const std::string& delimiter)
"\"\n\""
as a default value for delimiter
239+ estimate_mode='bTc/kB',
240+ )
241+ self.nodes[2].generate(1)
242+ self.sync_all(self.nodes[0:3])
243+ node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), explicit_fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
244+ assert_equal(self.nodes[2].getbalance(), node_2_bal)
69823cc nit (feel free to ignore) if you retouch, can avoid a getbalance
call here and also lines 270-271 below
0- node_2_bal = self.check_fee_amount(self.nodes[2].getbalance(), node_2_bal - Decimal('10'), explicit_fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
1- assert_equal(self.nodes[2].getbalance(), node_2_bal)
2+ balance = self.nodes[2].getbalance()
3+ node_2_bal = self.check_fee_amount(balance, node_2_bal - Decimal('10'), explicit_fee_per_byte, self.get_vsize(self.nodes[2].gettransaction(txid)['hex']))
4+ assert_equal(balance, node_2_bal)
122+ })
123+ # conf_target and totalFee (depends on -deprecatedrpc=totalFee)
124+ assert_raises_rpc_error(-3, "Unexpected key totalFee", node.bumpfee, txid, {
125+ "conf_target": 123,
126+ "totalFee": 456,
127+ })
totalFee
argument is now removed
0@@ -0,0 +1,11 @@
1+Updated or changed RPC
2+----------------------
3+
4+The `bumpfee`, `fundrawtransaction`, `sendmany`, `sendtoaddress`, and `walletcreatefundedpsbt`
5+RPC commands have been updated to include two new fee estimation methods "BTC/kB" and "sat/B".
6+The target is the fee expressed explicitly in the given form. Note that use of this feature
7+will trigger BIP 125 (replace-by-fee) opt-in.
8+
9+In addition, the estimate_mode parameter is now case insensitive for all of the above RPC commands.
estimate_mode
/
0@@ -0,0 +1,11 @@
1+Updated or changed RPC
2+----------------------
3+
4+The `bumpfee`, `fundrawtransaction`, `sendmany`, `sendtoaddress`, and `walletcreatefundedpsbt`
5+RPC commands have been updated to include two new fee estimation methods "BTC/kB" and "sat/B".
a
and b
were added.
420@@ -385,6 +421,8 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
421 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"donation\" \"seans outpost\"")
422 + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" true")
423 + HelpExampleRpc("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\", 0.1, \"donation\", \"seans outpost\"")
424+ + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" false true 0.00002 " + (CURRENCY_UNIT + "/kB"))
425+ + HelpExampleCli("sendtoaddress", "\"" + EXAMPLE_ADDRESS[0] + "\" 0.1 \"\" \"\" false true 2 " + (CURRENCY_ATOM + "/B"))
0fb63f0 these two lines should be placed before the HelpExampleRpc
in line 423, as currently the help prints:
0Examples:
1> bitcoin-cli sendtoaddress "bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl" 0.1
2> bitcoin-cli sendtoaddress "bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl" 0.1 "donation" "seans outpost"
3> bitcoin-cli sendtoaddress "bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl" 0.1 "" "" true
4> curl --user myusername --data-binary '{"jsonrpc": "1.0", "id": "curltest", "method": "sendtoaddress", "params": ["bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl", 0.1, "donation", "seans outpost"]}' -H 'content-type: text/plain;' http://127.0.0.1:8332/
5> bitcoin-cli sendtoaddress "bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl" 0.1 "" "" false true 0.00002 BTC/kB
6> bitcoin-cli sendtoaddress "bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl" 0.1 "" "" false true 2 sat/B
ACK 5ee0e46b93b2ca41eda9ee46fa59c6834689f0d5
Kudos on your patience on this useful feature. Feel free to ignore the comments below. The RPC examples could be updated (as you’ve done for sendtoaddress) and the tests could use some cleanup and refactoring, but nothing that can’t be done in a follow-up. Happy to re-ACK if you retouch.
Can verify move-only with:
git log -p -n1 --color-moved
This commit is move-only and doesn't change code or affect behavior.
* invalid parameter tests for bumpfee
* add tests for no conf_target explicit estimate_modes
ACK 25dac9fa65243ca8db02df2 I think this should be merged after all this time, even though it looks to me like there are needed follow-ups, fixes and test coverage to be added (see further down), which I don’t mind helping out with, if wanted.
Summary of the documentation of the changed RPCs:
bumpfee
fundrawtransaction
sendmany
sendtoaddress
walletcreatefundedpsbt
Thoughts:
I find this pretty confusing and worth harmonizing.
walletcreatefundedpsbt
does not work with feeRate
+ an explicit estimate mode. Not a surprise given the logic in FundTransaction
, wallet/rpcwallet.cpp::3005. Here are added tests in rpc_psbt.py
from line 154:
0 # Existing test.
1 # feeRate of 0.1 BTC / KB produces a total fee slightly below -maxtxfee (~0.05280000):
2 res = self.nodes[1].walletcreatefundedpsbt([{"txid":txid,"vout":p2wpkh_pos},{"txid":txid,"vout":p2sh_p2wpkh_pos},{"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0, {"feeRate": 0.1})
3 assert_greater_than(res["fee"], 0.05)
4 assert_greater_than(0.06, res["fee"])
5
6 # Same test using conf_target and estimate_mode btc/kb is green.
7 res = self.nodes[1].walletcreatefundedpsbt(
8 [{"txid":txid,"vout":p2wpkh_pos}, {"txid":txid,"vout":p2sh_p2wpkh_pos}, {"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0,
9 {
10 "conf_target": 0.1,
11 "estimate_mode": 'btc/kb',
12 }
13 )
14 assert_greater_than(res["fee"], 0.05)
15 assert_greater_than(0.06, res["fee"])
16
17 # Same test using conf_target and estimate_mode sat/b is green.
18 res = self.nodes[1].walletcreatefundedpsbt(
19 [{"txid":txid,"vout":p2wpkh_pos}, {"txid":txid,"vout":p2sh_p2wpkh_pos}, {"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0,
20 {
21 "conf_target": 10000,
22 "estimate_mode": 'sat/b',
23 }
24 )
25 assert_greater_than(res["fee"], 0.05)
26 assert_greater_than(0.06, res["fee"])
27
28 # But the same test using feeRate and estimate_mode btc/kb or
29 # sat/b fails with "Cannot specify both estimate_mode and feeRate".
30 res = self.nodes[1].walletcreatefundedpsbt(
31 [{"txid":txid,"vout":p2wpkh_pos}, {"txid":txid,"vout":p2sh_p2wpkh_pos}, {"txid":txid,"vout":p2pkh_pos}],
32 [{"txid":txid,"vout":p2wpkh_pos}, {"txid":txid,"vout":p2sh_p2wpkh_pos}, {"txid":txid,"vout":p2pkh_pos}], {self.nodes[1].getnewaddress():29.99}, 0,
33 {
34 "feeRate": 10000,
35 "estimate_mode": 'sat/b',
36 }
37 )
We need tests for fundrawtransaction
, walletcreatefundedpsbt
and bumpfee
.
We probably need to update the docs or code for fundrawtransaction
and walletcreatefundedpsbt
so that feeRate
with an explicit estimation mode either works or is stated as not permitted.
bumpfee
is suffering from the same issue as walletcreatefundedpsbt
. I’m totally for merging this and doing a follow-up PR to address those, if this gets enough momentum to finally be merged after all this time. Otherwise I’ll look into fixing those two.
3293@@ -3294,15 +3294,24 @@ static UniValue bumpfee(const JSONRPCRequest& request)
3294 RPCTypeCheckObj(options,
3295 {
3296 {"confTarget", UniValueType(UniValue::VNUM)},
3297+ {"conf_target", UniValueType(UniValue::VNUM)},
nit: Adding a comment here or below somewhere that this is acting as an alias to confTarget
would be helpful as well. I have not seen this pattern before and I am pretty sure it would confuse me. maybe in the line above:
0{"confTarget", UniValueType(UniValue::VNUM)}, // deprecated alias for conf_target
149@@ -127,9 +150,10 @@ def test_feerate_args(self, rbf_node, peer_node, dest_address):
150 self.sync_mempools((rbf_node, peer_node))
151 assert rbfid in rbf_node.getrawmempool() and rbfid in peer_node.getrawmempool()
152
153- assert_raises_rpc_error(-8, "confTarget can't be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.", rbf_node.bumpfee, rbfid, {"fee_rate": NORMAL, "confTarget": 1})
154+ assert_raises_rpc_error(-8, "conf_target can't be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.", rbf_node.bumpfee, rbfid, {"fee_rate": NORMAL, "confTarget": 1})
same below.
0 assert_raises_rpc_error(-8, "conf_target can't be set with fee_rate. Please provide either a confirmation target in blocks for automatic fee estimation, or an explicit fee rate.", rbf_node.bumpfee, rbfid, {"fee_rate": NORMAL, "conf_target": 1})
Code review ACK 25dac9fa65243ca8db02df22f484039c08114401
Just found two additional nits that can be ignored or kept for the follow-up.
Ouch, that image with the flashy white background hitting the eyes in dark mode :smile:
Strong preference for merging this and improving the rest in followups. This has been blocking #16378 for a while and rebasing/tweaking a big change can be a lot of work, also on reviewers.
:100:… @kallewoof has the patience of a sphinx. We’re still early enough in the release cycle to do the follow-ups.
Sorry to be so late with this review, but it seems that this overloads the conf_target
which used to be an integer (number of blocks) to also accept floats or strings. Then, if a float or string is passed, its unit is taken from the estimate mode (which used to be an enum string).
I believe, in the past all amounts (with a few exceptions) were passed as BTC and all feerates were passed as BTC/kB. If we start making feerates more type safe by forcing unit to be passed, then we should come up with a method that works for the whole code base and the whole RPC interface, not only for the few RPCs that happen to have two RPCArgs that can be recycled into passing a new type.
For example, in the future one could imagine an RPC to set the feefilter of a peer or to set the mempool min fee or simply settxfee
would be updated to allow passing a type-safe feerate.
Then having multiple ways to pass a feerate would be confusing and added complexity at the call site.
Also, passing the feerate (float) as an argument that used to be an integer is going to ask for mishaps, no?
While this is obviously an improvement, and I don’t want to block it, I also think that we should aim to get the RPC interface right the very first time, since it is impossible to change after release and hard to change in future releases.
Oh, looks like this got merged before I left my comment.
Anyway, I’d like to hear what the reviewers think of adding a new feerate
argument that simply passed the feerate (with unit) in one string. Then, parsing code could be shared among all places in the RPC code that use the new feerate type.
I suggested that a long time ago but iirc people didn’t like that idea…
On Thu, Jun 25, 2020, 2:00 PM MarcoFalke notifications@github.com wrote:
Oh, looks like this got merged before I left my comment.
Anyway, I’d like to hear what the reviewers think of adding a new feerate argument that simply passed the feerate (with unit) in one string. Then, parsing code could be shared among all places in the RPC code that use the new feerate type.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/bitcoin/bitcoin/pull/11413#issuecomment-649733071, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABMAFU3BKIFRU27KYLBKMELRYOGDXANCNFSM4D43FZIA .
Anyway, I’d like to hear what the reviewers think of adding a new feerate argument that simply passed the feerate (with unit) in one string. Then, parsing code could be shared among all places in the RPC code that use the new feerate type.
I do agree that this would be better defined. Overloading an argument on type seems somewhat brittle, and could lead to serious accidents. Sorry for merging I thought there was agreement.