Skip to content

Conversation

maflcko
Copy link
Member

@maflcko maflcko commented Dec 12, 2017

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.

@sipa
Copy link
Member

sipa commented Dec 12, 2017

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.

@jonasschnelli
Copy link
Contributor

Concept ACK.
I think the backward compatibility is solved in a good way... not sure if it should be mentioned somewhere (at least in some comments)

@promag
Copy link
Contributor

promag commented Dec 12, 2017

Should use deprecated flag to enable backward compatibility?

@laanwj
Copy link
Member

laanwj commented Dec 12, 2017

Concept ACK, I've frequently wondered why this was a dictionary.

Should use deprecated flag to enable backward compatibility?

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.

Copy link
Contributor

@promag promag left a 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)?

const bool outputs_is_array = request.params[1].isArray();
UniValue outputs = outputs_is_array ?
request.params[1].get_array() :
request.params[1].get_obj();
Copy link
Contributor

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();

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Contributor

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! :)

@promag
Copy link
Contributor

promag commented Dec 12, 2017

@laanwj so just add a note that the parameter as object is deprecated?

@laanwj
Copy link
Member

laanwj commented Dec 12, 2017

TODO: Needs tests

Something that I guess needs to be tested explicitly now: duplicate address causes failure (or is that an actual use-case? we don't allow multiple outputs to the same address in the wallet, at least).
Edit, a check and error for that already exists, #11877 adds a test.

@maflcko
Copy link
Member Author

maflcko commented Dec 12, 2017

Note that this didn't break any functional test, so it should be compatible with the previous interface.

@jnewbery
Copy link
Contributor

Concept ACK. I'll review once there are tests.

Have you seen luke-jr's change to allow RPC Type Checking against more than one type. It's in commit 8334875 in #11660. Would it be useful here?

@maflcko
Copy link
Member Author

maflcko commented Dec 13, 2017

Why? Invalid parameter, duplicated address is thrown.

Not when the dict implementation eats duplicate keys. This is at least the case for python's dict and javascript's dict.

Why does it matters?

The outputs structure is a vector, i.e. sorted in the bitcoin protocol, thus one could falsely assume that creating a raw transaction via createrawtransaction populates that vector in the same order as the outputs are typed.

Have you seen luke-jr's change to allow RPC Type Checking against more than one type. It's in commit 8334875 in #11660. Would it be useful here?

Yes, that could be useful. Though out of scope for this pull, imo. I am calling either get_array or get_obj, both of which throw on invalid type. So I am pretty sure I don't need to pull in further features.

C.f. https://dev.visucore.com/bitcoin/doxygen/univalue__get_8cpp_source.html#l00141

@laanwj
Copy link
Member

laanwj commented Dec 13, 2017

Not when the dict implementation eats duplicate keys. This is at least the case for python's dict and javascript's dict.

Normally whatever dict/object implementation is on the client-side will eat it.
To be able pass duplicate keys, the clients need to some way emit an array as a JSON object.
That's absolutely against the JSON spec, just like expecting object attributes to be emitted in a certain order. To quote myself from IRC:

<wumpus> FWIW in principle it is already possible to specify the order of outputs, but only if the client-side JSON library can emit ordered dictionaries. Python's can do this when you use Collections.OrderedDict() instead of a dictionary. The server-side (univalue) won't reorder dictionaries. But using an array is conforming to the spec instead of 'it happens to work'.

Note that this didn't break any functional test, so it should be compatible with the previous interface.

It seems to break Travis, any idea why (or is this another fluke?).

@jnewbery
Copy link
Contributor

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:

BOOST_CHECK_THROW(CallRPC("createrawtransaction [] []"), std::runtime_error);

(src/test/rpc_tests.cpp, L55)

since createrawtransaction can now take an array as the second argument.

@maflcko
Copy link
Member Author

maflcko commented Dec 13, 2017

I will rebase and fixup all of this when the other createrawtransaction test are merged.

@instagibbs
Copy link
Member

concept ACK

@maflcko maflcko force-pushed the Mf1712-rpcCreateRawSortedOuts branch 7 times, most recently from fafdc08 to fa288ad Compare December 13, 2017 21:46
@promag
Copy link
Contributor

promag commented Dec 14, 2017

BTW, echoing @laanwj #8457 (comment). I understand that here it's a different case though.

@maflcko
Copy link
Member Author

maflcko commented Dec 14, 2017

@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.

@promag
Copy link
Contributor

promag commented Dec 14, 2017

Just wanted to link his concern regarding overloading. However here we want to fix a problem which happens to overload the RPC.

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");
Copy link
Contributor

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];
Copy link
Contributor

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.

Copy link
Member Author

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

@maflcko
Copy link
Member Author

maflcko commented Feb 7, 2018

Rebased

@maflcko maflcko force-pushed the Mf1712-rpcCreateRawSortedOuts branch from fabf801 to fa2d1df Compare February 22, 2018 22:37
@maflcko maflcko force-pushed the Mf1712-rpcCreateRawSortedOuts branch from fa2d1df to faf766d Compare February 22, 2018 22:40
@maflcko
Copy link
Member Author

maflcko commented Feb 22, 2018

Added release notes.

@luke-jr
Copy link
Member

luke-jr commented Feb 26, 2018

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...?

@maflcko
Copy link
Member Author

maflcko commented Feb 26, 2018

@luke-jr The check for duplicate keys is already there. See "Invalid parameter, duplicated address" in the functional tests.

Copy link
Contributor

@jnewbery jnewbery left a 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 */
Copy link
Contributor

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);
Copy link
Contributor

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.

Copy link
Member Author

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

Copy link
Contributor

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()?

Copy link
Member Author

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...

Copy link
Contributor

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).

Copy link
Member Author

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.

@@ -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.
Copy link
Contributor

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

Copy link
Contributor

@promag promag left a 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"
Copy link
Contributor

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.

Copy link
Contributor

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.
   ]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed alignment

@maflcko maflcko force-pushed the Mf1712-rpcCreateRawSortedOuts branch from fa7a11a to fa06dfc Compare March 7, 2018 17:58
@maflcko
Copy link
Member Author

maflcko commented Mar 7, 2018

Force pushed the documentation changes.

@jnewbery
Copy link
Contributor

jnewbery commented Mar 7, 2018

Tested ACK fa06dfc.

I'd still prefer to modify the unit test rather than dropping it, but this is ready for merge.

@promag
Copy link
Contributor

promag commented Mar 7, 2018

Other than the 1 space indentation nit:

2. "outputs"               (array, required) a json array with outputs (key-value pairs)
   [
    {

utACK fa06dfc.

@@ -352,14 +358,25 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
+ HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"")
Copy link
Contributor

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

@conscott
Copy link
Contributor

Tested ACK fa06dfc

Just left small inline comment

@maflcko
Copy link
Member Author

maflcko commented Mar 11, 2018

Added a commit to address the documentation nit. This seems ready for merge.

@laanwj laanwj merged commit fac7013 into bitcoin:master Mar 13, 2018
laanwj added a commit that referenced this pull request Mar 13, 2018
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
@maflcko maflcko deleted the Mf1712-rpcCreateRawSortedOuts branch March 13, 2018 18:19
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jun 10, 2020
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
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jun 13, 2020
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
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jun 13, 2020
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
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jun 13, 2020
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
PastaPastaPasta pushed a commit to PastaPastaPasta/dash that referenced this pull request Jun 17, 2020
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
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Dec 16, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants