hash_or_height
argument to getblockstats
),
this avoids the need for bitcoin-cli users to use double JSON quoting+shell
quoting to pass the string.
hash_or_height
argument to getblockstats
),
this avoids the need for bitcoin-cli users to use double JSON quoting+shell
quoting to pass the string.
Just pass arguments that can't be parsed as JSON as string arguments. If the
RPC method isn't expecting a string argument, this will result in more specific
errors from the RPC. And if it allows an argument to be a string as one
possible type (for example the `hash_or_height` argument to `getblockstats`),
this avoids the need for bitcoin-cli users to use double JSON quoting+shell
quoting to pass the string.
I think this approach is fine, unless there is an use case where the end result would be really bad - which I don’t believe there is.
Concept ACK from me.
ParseNonRFCJSONValue
should be removed
218@@ -219,12 +219,13 @@ UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::s
219 for (unsigned int idx = 0; idx < strParams.size(); idx++) {
220 const std::string& strVal = strParams[idx];
221
222- if (!rpcCvtTable.convert(strMethod, idx)) {
223+ UniValue json_value;
224+ if (!rpcCvtTable.convert(strMethod, idx) || !json_value.read(strVal)) {
Seems odd to have two inversions in a condition. Would be easier to write
0if convert() and read():
1 # push back parsed value
2else:
3 # push back str
Tested ACK 45c743
This is effectively moving the type check from the client to the server (unparsable json will get send as string). I guess this is fine
0# type check is moved to server
1$ for i in {master,45c743}; do ./src/bitcoin-cli-$i -regtest getrawtransaction ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff True ffff ; done
2error: Error parsing JSON:True
3
4error code: -1
5error message:
6JSON value is not a boolean as expected
7
8
9# test conversion
10$ for i in {master,45c743}; do ./src/bitcoin-cli-$i -regtest echojson true 1 '"str"' '[1,2]' '{"a":1}' ; done
11[
12 true,
13 1,
14 "str",
15 [
16 1,
17 2
18 ],
19 {
20 "a": 1
21 }
22]
23[
24 true,
25 1,
26 "str",
27 [
28 1,
29 2
30 ],
31 {
32 "a": 1
33 }
34]
35
36# test no conversion
37$ for i in {master,45c743}; do ./src/bitcoin-cli-$i -regtest echo true 1 '"str"' '[1,2]' '{"a":1}' ; done
38[
39 "true",
40 "1",
41 "\"str\"",
42 "[1,2]",
43 "{\"a\":1}"
44]
45[
46 "true",
47 "1",
48 "\"str\"",
49 "[1,2]",
50 "{\"a\":1}"
51]
52
53# test conversion with invalid json
54$ for i in {master,45c743}; do ./src/bitcoin-cli-$i -regtest echojson True ; done
55error: Error parsing JSON:True
56[
57 "True"
58]
This is effectively moving the type check from the client to the server (unparsable json will get send as string)
Agree. This matches a common frontend validation pattern:
The server must validate and reject garbage. (1) Thus one option is to lean on the remote server to perform that validation.
However, it is often better UX – and potentially saves users $$ in bandwidth, and saves server CPU cycles – to (2) avoid sending data the client can prove, via its own validation, is known garbage data.
Yes, this is double validation; but the 90+% common case is rejected on the client side, remaining efficient by sending no garbage over the network.
Will wait for concept acks & nacks before working more on this. To summarize comments so far:
Advantages of this change
bitcoin-cli getblockstats
command, no longer requiring double json+shell quoting. Other rpc methods could benefit similarly since nothing about this change is specific to getblockstats
.Disadvantages of this change
bitcoin-cli
error reporting is bad now, but if it’s removed entirely, there’s loss of opportunity to improve it in the future.Potential followups
Concept ACK
In this case I think we should optimise for user-friendliness.
ParseNonRFCJSONValue
, so it can be removed.
244@@ -244,12 +245,13 @@ UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<s
245 std::string name = s.substr(0, pos);
246 std::string value = s.substr(pos+1);
247
248- if (!rpcCvtTable.convert(strMethod, name)) {
249+ UniValue json_value;
250+ if (!rpcCvtTable.convert(strMethod, name) || !json_value.read(value)) {
ACK 45c7439fed93dc31c7bc79906ae6c2e9bf062f8c, above comment #15448 (comment) looks good.
Should have python test for new bitcoin-cli behavior.
👍
has resulted in quite some unexpected disasters. @laanwj like what? Preventing disasters and client validation sound like 2 different things and pretty much unrelated. But better safe than sorry? No strong opinion.
@laanwj like what? Preventing disasters and client validation sound like 2 different things and pretty much unrelated. But better safe than sorry? No strong opinion.
Defense in depth, belt and suspenders, etc.
I mean the underlying issue is that there is an ambiguity, and resolving this ambiguity is here moved from the client to the server, which isn’t always better. It’s better if both the client and server know what the interpretation of some value is. SQL injection attacks are a well-known problem that is caused by trusting that the server will the right thing when you just pass the value as-is.
""
- it fallbacks the argument to string which seems natural considering this is a command line tool.
@ryanofsky just wrote 4c19cf574e15b1bf0bd6142dfa55c2a0f637209e but then I’ve realised it’s pretty much the same as this.
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.
@laanwj this just pretends the client typed "" - it fallbacks the argument to string which seems natural considering this is a command line tool.
That’s my problem with it “pretends the client typed”. If it is purely a user-facing tool like a GUI it might be different (still, it’s often better not to guess at intent). The problem is that tools like this are also used in scripts and such, and they might deal with (partially) untrusted input. I think determinism and predictability is very important in input processing.