-
Notifications
You must be signed in to change notification settings - Fork 37.7k
[rpc] createrawtransaction: Accept sorted outputs #11872
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Vague concept ACK that accepting a list instead of a dict should be supported. I have little opinion on how to do that in a backward compatible way. |
Concept ACK. |
Should use deprecated flag to enable backward compatibility? |
Concept ACK, I've frequently wondered why this was a dictionary.
I think both ways should be supported for the foreseeable future, no need to deprecate anything right now. There's no reason to break software for this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In case of duplicate keys, either of them might silently disappear, with no user feedback at all
Why? Invalid parameter, duplicated address
is thrown.
A dictionary does not guarantee that keys are sorted
Why does it matters?
Are these concerns because of the common lack support for duplicate keys in clients?
And since this is an utility call, weren't we moving enhancements to bitcoin-tx instead (although I'm ok with it)?
Maybe the missing piece is an updaterawtransaction
call, where inputs/outputs can be added/removed/updated (just a thought)?
src/rpc/rawtransaction.cpp
Outdated
const bool outputs_is_array = request.params[1].isArray(); | ||
UniValue outputs = outputs_is_array ? | ||
request.params[1].get_array() : | ||
request.params[1].get_obj(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will raise JSON value is not an object as expected
which doesn't match with the new signature. I suggest to switch to isObject()
above so that get_array
throws. In that case, it is not necessary to callget_obj()
:
... = outputs_is_object ? request.params[1] : request.params[1].get_array();
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you provide code to reproduce that throw, please. When I tested it a day ago in the gui, it didn't throw.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@promag Heh, now I see what you mean. Makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Cool, I forgot to reply! :)
@laanwj so just add a note that the parameter as object is deprecated? |
|
Note that this didn't break any functional test, so it should be compatible with the previous interface. |
Not when the dict implementation eats duplicate keys. This is at least the case for python's dict and javascript's dict.
The outputs structure is a vector, i.e. sorted in the bitcoin protocol, thus one could falsely assume that creating a raw transaction via
Yes, that could be useful. Though out of scope for this pull, imo. I am calling either C.f. https://dev.visucore.com/bitcoin/doxygen/univalue__get_8cpp_source.html#l00141 |
Normally whatever dict/object implementation is on the client-side will eat it.
It seems to break Travis, any idea why (or is this another fluke?). |
No, not a random Travis failure. This rpc unit test is broken:
(src/test/rpc_tests.cpp, L55) since |
I will rebase and fixup all of this when the other createrawtransaction test are merged. |
concept ACK |
fafdc08
to
fa288ad
Compare
BTW, echoing @laanwj #8457 (comment). I understand that here it's a different case though. |
@promag Can you elaborate a bit more on that? It is a different case and in fact we do overload already, IIRC for some bool/integer parameters. |
Just wanted to link his concern regarding overloading. However here we want to fix a problem which happens to overload the RPC. |
src/rpc/rawtransaction.cpp
Outdated
for (size_t i = 0; i < outputs.size(); ++i) { | ||
const UniValue& output = outputs[i]; | ||
if (output.size() != 1) { | ||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter: Key-value pair must contain exactly one key"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Current format: Invalid parameter, key-value pair ...
.
// Translate array of key-value pairs into dict | ||
UniValue outputs_dict = UniValue(UniValue::VOBJ); | ||
for (size_t i = 0; i < outputs.size(); ++i) { | ||
const UniValue& output = outputs[i]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validate output is an object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Also added a test for this
Rebased |
fabf801
to
fa2d1df
Compare
fa2d1df
to
faf766d
Compare
Added release notes. |
I don't see the use case... it might make sense for error checking, but this doesn't add a check for duplicate keys either...? |
@luke-jr The check for duplicate keys is already there. See "Invalid parameter, duplicated address" in the functional tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
utACK fa7a11a4c798eacc984762a379c206b0c7da8723
A few nits inline, but none should delay merge.
src/rpc/server.h
Outdated
@@ -30,7 +30,7 @@ namespace RPCServer | |||
/** Wrapper for UniValue::VType, which includes typeAny: | |||
* Used to denote don't care type. Only used by RPCTypeCheckObj */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment could be updated since this object is now used by RPCTypeCheck
and RPCTypeCheckArgument
@@ -52,7 +52,6 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams) | |||
BOOST_CHECK_THROW(CallRPC("createrawtransaction"), std::runtime_error); | |||
BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), std::runtime_error); | |||
BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), std::runtime_error); | |||
BOOST_CHECK_THROW(CallRPC("createrawtransaction [] []"), std::runtime_error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just change from BOOST_CHECK_THROW()
to BOOST_CHECK_NO_THROW()
instead of removing entirely? Passing two arrays is now permitted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thx. Moved the check to the functional tests
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not change to BOOST_CHECK_NO_THROW()
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jnewbery I think we deprecated the existing rpc unit tests in favor of the functional tests which test the rpc interface. If you wanted reasonable coverage, you'd have to duplicate all the rpc unit tests based on the functional rpc tests. So yeah, I am not convinced that the unit tests add additional coverage...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you really think the unit tests are deprecated, they should be removed in a separate PR. It doesn't make sense to me to remove just this one case in this PR.
Anyway, as I said - I don't think this should hold up merge. I'm just puzzled that you'd chose a larger diff that reduces test coverage (the fact that this unit test failed in the first iteration of this PR tells me that it was testing something).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Moving all those tests to the functional test suite makes sense.
doc/release-notes.md
Outdated
@@ -61,7 +61,8 @@ RPC changes | |||
|
|||
### Low-level changes | |||
|
|||
- The `fundrawtransaction` rpc will reject the previously deprecated `reserveChangeKey` option. | |||
- The `fundrawtransaction` RPC will reject the previously deprecated `reserveChangeKey` option. | |||
- The `createrawtransaction` RPC will now accept an array or dictionary (kept for compatibility) for the `outputs` parameter. This makes the result of the call deterministic. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could use simpler language and be more explicit makes the result of the call deterministic
-> means the order of transaction outputs can be specified by the client
or similar
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a use case to create a raw transaction without outputs?
@@ -335,12 +336,17 @@ UniValue createrawtransaction(const JSONRPCRequest& request) | |||
" } \n" | |||
" ,...\n" | |||
" ]\n" | |||
"2. \"outputs\" (object, required) a json object with outputs\n" | |||
"2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n" | |||
" [\n" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit, the indentation could be fixed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The alignment too:
1. "inputs" (array, required) A json array of json objects
[
{
"txid":"id", (string, required) The transaction id
"vout":n, (numeric, required) The output number
"sequence":n (numeric, optional) The sequence number
}
,...
]
2. "outputs" (array, required) a json array with outputs (key-value pairs)
[
{
"address": x.xxx, (obj, optional) A key-value pair. The key (string) is the bitcoin address, the value (float or string) is the amount in BTC
},
{
"data": "hex" (obj, optional) A key-value pair. The key must be "data", the value is hex encoded data
}
,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also
accepted as second parameter.
]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed alignment
fa7a11a
to
fa06dfc
Compare
Force pushed the documentation changes. |
Tested ACK fa06dfc. I'd still prefer to modify the unit test rather than dropping it, but this is ready for merge. |
Other than the 1 space indentation nit:
utACK fa06dfc. |
src/rpc/rawtransaction.cpp
Outdated
@@ -352,14 +358,25 @@ UniValue createrawtransaction(const JSONRPCRequest& request) | |||
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to add an example for the sorted format?
Right now it only shows examples with the 'backwards compatible" format
Tested ACK fa06dfc Just left small inline comment |
Added a commit to address the documentation nit. This seems ready for merge. |
fac7013 rpc: Update createrawtransaction examples (MarcoFalke) fa06dfc [rpc] createrawtransaction: Accept sorted outputs (MarcoFalke) 8acd25d rpc: Allow typeAny in RPCTypeCheck (MarcoFalke) Pull request description: The second parameter of the `createrawtransaction` is a dictionary of the outputs. This comes with at least two drawbacks: * In case of duplicate keys, either of them might silently disappear, with no user feedback at all. A user needs to make other mistakes, but this could eventually lead to abnormal tx fees. * A dictionary does not guarantee that keys are sorted. Again, a user needs to keep this in mind, as it could eventually lead to excessive tx fees. Even though my scenario of loss-of-funds is unlikely to happen, I see it as a inconvenience that should be fixed. Tree-SHA512: cd562f34f7f9f79c7d3433805971325c388c2035611be283980f4049066a622df4f0afdc11d7ac96662260ec0115147cb65e1ab5268f5a1b063242f3fe425f77
fac7013 rpc: Update createrawtransaction examples (MarcoFalke) fa06dfc [rpc] createrawtransaction: Accept sorted outputs (MarcoFalke) 8acd25d rpc: Allow typeAny in RPCTypeCheck (MarcoFalke) Pull request description: The second parameter of the `createrawtransaction` is a dictionary of the outputs. This comes with at least two drawbacks: * In case of duplicate keys, either of them might silently disappear, with no user feedback at all. A user needs to make other mistakes, but this could eventually lead to abnormal tx fees. * A dictionary does not guarantee that keys are sorted. Again, a user needs to keep this in mind, as it could eventually lead to excessive tx fees. Even though my scenario of loss-of-funds is unlikely to happen, I see it as a inconvenience that should be fixed. Tree-SHA512: cd562f34f7f9f79c7d3433805971325c388c2035611be283980f4049066a622df4f0afdc11d7ac96662260ec0115147cb65e1ab5268f5a1b063242f3fe425f77
fac7013 rpc: Update createrawtransaction examples (MarcoFalke) fa06dfc [rpc] createrawtransaction: Accept sorted outputs (MarcoFalke) 8acd25d rpc: Allow typeAny in RPCTypeCheck (MarcoFalke) Pull request description: The second parameter of the `createrawtransaction` is a dictionary of the outputs. This comes with at least two drawbacks: * In case of duplicate keys, either of them might silently disappear, with no user feedback at all. A user needs to make other mistakes, but this could eventually lead to abnormal tx fees. * A dictionary does not guarantee that keys are sorted. Again, a user needs to keep this in mind, as it could eventually lead to excessive tx fees. Even though my scenario of loss-of-funds is unlikely to happen, I see it as a inconvenience that should be fixed. Tree-SHA512: cd562f34f7f9f79c7d3433805971325c388c2035611be283980f4049066a622df4f0afdc11d7ac96662260ec0115147cb65e1ab5268f5a1b063242f3fe425f77
fac7013 rpc: Update createrawtransaction examples (MarcoFalke) fa06dfc [rpc] createrawtransaction: Accept sorted outputs (MarcoFalke) 8acd25d rpc: Allow typeAny in RPCTypeCheck (MarcoFalke) Pull request description: The second parameter of the `createrawtransaction` is a dictionary of the outputs. This comes with at least two drawbacks: * In case of duplicate keys, either of them might silently disappear, with no user feedback at all. A user needs to make other mistakes, but this could eventually lead to abnormal tx fees. * A dictionary does not guarantee that keys are sorted. Again, a user needs to keep this in mind, as it could eventually lead to excessive tx fees. Even though my scenario of loss-of-funds is unlikely to happen, I see it as a inconvenience that should be fixed. Tree-SHA512: cd562f34f7f9f79c7d3433805971325c388c2035611be283980f4049066a622df4f0afdc11d7ac96662260ec0115147cb65e1ab5268f5a1b063242f3fe425f77
fac7013 rpc: Update createrawtransaction examples (MarcoFalke) fa06dfc [rpc] createrawtransaction: Accept sorted outputs (MarcoFalke) 8acd25d rpc: Allow typeAny in RPCTypeCheck (MarcoFalke) Pull request description: The second parameter of the `createrawtransaction` is a dictionary of the outputs. This comes with at least two drawbacks: * In case of duplicate keys, either of them might silently disappear, with no user feedback at all. A user needs to make other mistakes, but this could eventually lead to abnormal tx fees. * A dictionary does not guarantee that keys are sorted. Again, a user needs to keep this in mind, as it could eventually lead to excessive tx fees. Even though my scenario of loss-of-funds is unlikely to happen, I see it as a inconvenience that should be fixed. Tree-SHA512: cd562f34f7f9f79c7d3433805971325c388c2035611be283980f4049066a622df4f0afdc11d7ac96662260ec0115147cb65e1ab5268f5a1b063242f3fe425f77
fac7013 rpc: Update createrawtransaction examples (MarcoFalke) fa06dfc [rpc] createrawtransaction: Accept sorted outputs (MarcoFalke) 8acd25d rpc: Allow typeAny in RPCTypeCheck (MarcoFalke) Pull request description: The second parameter of the `createrawtransaction` is a dictionary of the outputs. This comes with at least two drawbacks: * In case of duplicate keys, either of them might silently disappear, with no user feedback at all. A user needs to make other mistakes, but this could eventually lead to abnormal tx fees. * A dictionary does not guarantee that keys are sorted. Again, a user needs to keep this in mind, as it could eventually lead to excessive tx fees. Even though my scenario of loss-of-funds is unlikely to happen, I see it as a inconvenience that should be fixed. Tree-SHA512: cd562f34f7f9f79c7d3433805971325c388c2035611be283980f4049066a622df4f0afdc11d7ac96662260ec0115147cb65e1ab5268f5a1b063242f3fe425f77
The second parameter of the
createrawtransaction
is a dictionary of the outputs. This comes with at least two drawbacks:Even though my scenario of loss-of-funds is unlikely to happen, I see it as a inconvenience that should be fixed.