0==None
and ""==None
is confusing, unneeded and undocumented
0==None
and ""==None
is confusing, unneeded and undocumented
212@@ -213,10 +213,10 @@ static std::string LabelFromValue(const UniValue& value)
213 static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee)
Would need to update the SetFeeEstimateMode
Doxygen documentation, e.g.:
0- * [@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;
1- * if a fee_rate is present, 0 is allowed here as a no-op positional placeholder
2- * [@param](/bitcoin-bitcoin/contributor/param/)[in] estimate_mode UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative";
3- * if a fee_rate is present, "" is allowed here as a no-op positional placeholder
4- * [@param](/bitcoin-bitcoin/contributor/param/)[in] fee_rate UniValue real; fee rate in sat/vB;
5- * if a fee_rate is present, both conf_target and estimate_mode must either be null, or no-op
6+ * [@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
7+ * [@param](/bitcoin-bitcoin/contributor/param/)[in] estimate_mode UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative"
8+ * [@param](/bitcoin-bitcoin/contributor/param/)[in] fee_rate UniValue real; fee rate in sat/vB; if present, both conf_target and estimate_mode must be null
9 * [@param](/bitcoin-bitcoin/contributor/param/)[in] override_min_fee bool; whether to set fOverrideFeeRate to true to disable minimum fee rate checks and instead
10- * verify only that fee_rate is greater than 0
11+ * verify only that fee_rate is greater than 0
12 * [@throws](/bitcoin-bitcoin/contributor/throws/) a JSONRPCError if conf_target, estimate_mode, or fee_rate contain invalid values or are in conflict
13 */
14 static void SetFeeEstimateMode(const CWallet* pwallet, CCoinControl& cc, const UniValue& conf_target, const UniValue& estimate_mode, const UniValue& fee_rate, bool override_min_fee)
Would this change not disable using fee_rate
as a positional argument?
The RPCExamples for sendtoaddress
and send
would need to be updated if this change is made, as the positional fee_rate
usage is documented in them.
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.
Oh, it looks like bitcoin-cli can not properly pass JSON-None? Sounds like a bug
You can just use null
. It’s worked for me before.
null
to "null"
(string), not Null
(null-type)
The RPCExamples for sendtoaddress and send would need to be updated
Thanks, done.
202+ * @param[in] wallet Wallet reference
203 * @param[in,out] cc Coin control to be updated
204 * @param[in] conf_target UniValue integer; confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h;
205- * if a fee_rate is present, 0 is allowed here as a no-op positional placeholder
206 * @param[in] estimate_mode UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative";
207 * if a fee_rate is present, "" is allowed here as a no-op positional placeholder
Maybe we should also allow an estimate mode of “unset” with a fee rate?
I wonder if the helps should state that "" is an allowed no-op value in addition to unset, economical and conservative.
234@@ -235,8 +235,7 @@ def run_test(self):
235 fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
236 explicit_fee_rate_btc_kvb = Decimal(fee_rate_btc_kvb) / 1000
237
238- # Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
239- txid = self.nodes[2].sendmany(amounts={address: 10}, conf_target=0, estimate_mode="", fee_rate=fee_rate_sat_vb)
240+ txid = self.nodes[2].sendmany(amounts={address: 10}, fee_rate=fee_rate_sat_vb)
re-enable previous coverage?
0# Passing conf_target null, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
1txid = self.nodes[2].sendmany(amounts={address: 10}, conf_target=None, estimate_mode="", fee_rate=fee_rate_sat_vb)
405@@ -407,8 +406,7 @@ def run_test(self):
406 fee_rate_sat_vb = 2
407 fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
408
409- # Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
410- txid = self.nodes[2].sendtoaddress(address=address, amount=amount, conf_target=0, estimate_mode="", fee_rate=fee_rate_sat_vb)
411+ txid = self.nodes[2].sendtoaddress(address=address, amount=amount, fee_rate=fee_rate_sat_vb)
0# Passing conf_target null, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
1txid = self.nodes[2].sendtoaddress(address=address, amount=amount, conf_target=None, estimate_mode="", fee_rate=fee_rate_sat_vb)
259@@ -260,17 +260,15 @@ def run_test(self):
260 res2 = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=1, add_to_wallet=False)
261 assert_equal(self.nodes[1].decodepsbt(res1["psbt"])["fee"], self.nodes[1].decodepsbt(res2["psbt"])["fee"])
262
263- # Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
264- res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=0, estimate_mode="", fee_rate=7, add_to_wallet=False)
265+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, fee_rate=7, add_to_wallet=False)
0# Passing conf_target null, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
1res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, conf_target=None, estimate_mode="", fee_rate=7, add_to_wallet=False)
270 fee = self.nodes[1].decodepsbt(res["psbt"])["fee"]
271 assert_fee_amount(fee, Decimal(len(res["hex"]) / 2), Decimal("0.00002"))
272
273- # Passing conf_target 0, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
274- res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=0, arg_estimate_mode="", arg_fee_rate=4.531, add_to_wallet=False)
275+ res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_fee_rate=4.531, add_to_wallet=False)
0# Passing conf_target null, estimate_mode "" as placeholder arguments should allow fee_rate to apply.
1res = self.test_send(from_wallet=w0, to_wallet=w1, amount=1, arg_conf_target=None, arg_estimate_mode="", arg_fee_rate=4.531, add_to_wallet=False)
It doesn’t work for strings because cli will convert null to “null” (string), not Null (null-type)
Yes, in that case you cannot pass any other kind of JSON type except strings. It’s kind of annoying, but there is no non-ambigious way of doing so because every argument is a valid string. Special-casing the string “null” in bitcoin-cli would be awful. At least named arguments can achieve this.
send
RPC, it seems impossible to use “options” as a positional arg, as there is no no-op value for the “fee_rate” arg. So the third example should either be removed, or updated if we fix this via either allowing “fee_rate” to have a no-op value or, more likely, by moving “fee_rate” to after “options” in the args order. I can do this in a fee_rate follow-up PR if not done here.
Do you have steps to reproduce? This works fine locally:
0$ ./src/bitcoin-cli send '{"bcrt1q0pqmg4cvjf80x7l7xsrhp65fchg2fw9zk39m74":0.1}' null "unset" null '{"fee_rate":1.234}'
1{
2 "txid": "3bda2cb650a3937b676782214837fea6313158a156113e059bd9cad118717ecb",
3 "complete": true
4}
Oh good, thanks, sorry for noise. One of my “null"s was spelled “nul”. I think the third week of full lockdown and lack of my usual balance from outdoor sport are getting to me…
0$ ./src/bitcoin-cli -signet -rpcwallet="" send '{"tb1qn84cdagw67kpqaz4ugmw76tlhqgdp2jk9zz94j": 0.05}' null "unset" null '{"fee_rate": 1}'
1{
2 "txid": "25d0924beac73778566909c0658698a4602d2f9444895542f85726e7b5f22465",
3 "complete": true
4}
This send
example was incorrect and needs to be updated:
0Send 0.2 BTC with a fee rate of 1 sat/vB using the options argument
1> bitcoin-cli send '{"bc1q09vm5lfy0j5reeulh4x5752q25uqqvz34hufdl": 0.2}' '{"fee_rate": 1}'
200@@ -201,22 +201,20 @@ static std::string LabelFromValue(const UniValue& value)
201 * @param[in] wallet Wallet reference
202 * @param[in,out] cc Coin control to be updated
203 * @param[in] conf_target UniValue integer; confirmation target in blocks, values between 1 and 1008 are valid per policy/fees.h;
204- * if a fee_rate is present, 0 is allowed here as a no-op positional placeholder
205 * @param[in] estimate_mode UniValue string; fee estimation mode, valid values are "unset", "economical" or "conservative";
206- * if a fee_rate is present, "" is allowed here as a no-op positional placeholder
207 * @param[in] fee_rate UniValue real; fee rate in sat/vB;
208- * if a fee_rate is present, both conf_target and estimate_mode must either be null, or no-op
209+ * if present, both conf_target and estimate_mode must either be null, or "unset"
estimate_mode
can’t be null
for send
and sendtoaddress
. It will throw Cannot specify both estimate_mode and fee_rate
. It does if you add it to client.cpp
, but then unset
and "unset"
throw error: Error parsing JSON: unset
.
estimate_mode can’t be null for send and sendtoaddress. It will throw Cannot specify both estimate_mode and fee_rate.
This comment is in the server code, where it can be null, not in the client code. You can trivially check with this diff, so I think the comment is correct:
0diff --git a/test/functional/wallet_basic.py b/test/functional/wallet_basic.py
1index ac4a6e4948..17c013d26a 100755
2--- a/test/functional/wallet_basic.py
3+++ b/test/functional/wallet_basic.py
4@@ -235,7 +235,7 @@ class WalletTest(BitcoinTestFramework):
5 fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
6 explicit_fee_rate_btc_kvb = Decimal(fee_rate_btc_kvb) / 1000
7
8- txid = self.nodes[2].sendmany(amounts={address: 10}, fee_rate=fee_rate_sat_vb)
9+ txid = self.nodes[2].sendmany(amounts={address: 10}, fee_rate=fee_rate_sat_vb, estimate_mode=None)
10 self.nodes[2].generate(1)
11 self.sync_all(self.nodes[0:3])
12 balance = self.nodes[2].getbalance()
13@@ -406,7 +406,7 @@ class WalletTest(BitcoinTestFramework):
14 fee_rate_sat_vb = 2
15 fee_rate_btc_kvb = fee_rate_sat_vb * 1e3 / 1e8
16
17- txid = self.nodes[2].sendtoaddress(address=address, amount=amount, fee_rate=fee_rate_sat_vb)
18+ txid = self.nodes[2].sendtoaddress(address=address, amount=amount, fee_rate=fee_rate_sat_vb, estimate_mode=None)
19 tx_size = self.get_vsize(self.nodes[2].gettransaction(txid)['hex'])
20 self.nodes[0].generate(1)
21 self.sync_all(self.nodes[0:3])
tACK fa69c2c78455fd0dc436018fece9ff7fc83a180d modulo unset
I don’t think treating ""==None
is particularly ambiguous, but null
is not too bad.
I don’t think treating “"==None is particularly ambiguous
Fair enough, but it wasn’t documented in the RPCArg help. I think keeping the already existing "unset"
is sufficient and adding more alternative strings to do the same thing isn’t going to help.