aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/rawtransaction.cpp
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2018-03-13 17:58:52 +0100
committerWladimir J. van der Laan <laanwj@gmail.com>2018-03-13 18:00:06 +0100
commit702e8b70bd870f506ce8157e58b77cb7d0a0f98d (patch)
tree089aad138eced3caaf758d325214c28d1b83cc35 /src/rpc/rawtransaction.cpp
parent0f0229d0c36310eea329716fbc81541c7c3a62b1 (diff)
parentfac70134a9e3a1fc8e491aecb3c36d0184ba7b82 (diff)
downloadbitcoin-702e8b70bd870f506ce8157e58b77cb7d0a0f98d.tar.xz
Merge #11872: [rpc] createrawtransaction: Accept sorted outputs
fac70134a rpc: Update createrawtransaction examples (MarcoFalke) fa06dfce0 [rpc] createrawtransaction: Accept sorted outputs (MarcoFalke) 8acd25d85 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
Diffstat (limited to 'src/rpc/rawtransaction.cpp')
-rw-r--r--src/rpc/rawtransaction.cpp66
1 files changed, 48 insertions, 18 deletions
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 8dcfb48e9a..883ec71747 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -316,9 +316,10 @@ UniValue verifytxoutproof(const JSONRPCRequest& request)
UniValue createrawtransaction(const JSONRPCRequest& request)
{
- if (request.fHelp || request.params.size() < 2 || request.params.size() > 4)
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 4) {
throw std::runtime_error(
- "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...} ( locktime ) ( replaceable )\n"
+ // clang-format off
+ "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] [{\"address\":amount},{\"data\":\"hex\"},...] ( locktime ) ( replaceable )\n"
"\nCreate a transaction spending the given inputs and creating new outputs.\n"
"Outputs can be addresses or data.\n"
"Returns hex-encoded raw transaction.\n"
@@ -329,18 +330,23 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
"1. \"inputs\" (array, required) A json array of json objects\n"
" [\n"
" {\n"
- " \"txid\":\"id\", (string, required) The transaction id\n"
+ " \"txid\":\"id\", (string, required) The transaction id\n"
" \"vout\":n, (numeric, required) The output number\n"
" \"sequence\":n (numeric, optional) The sequence number\n"
" } \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"
" {\n"
- " \"address\": x.xxx, (numeric or string, required) The key is the bitcoin address, the numeric value (can be string) is the " + CURRENCY_UNIT + " amount\n"
- " \"data\": \"hex\" (string, required) The key is \"data\", the value is hex encoded data\n"
- " ,...\n"
+ " \"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 " + CURRENCY_UNIT + "\n"
+ " },\n"
+ " {\n"
+ " \"data\": \"hex\" (obj, optional) A key-value pair. The key must be \"data\", the value is hex encoded data\n"
" }\n"
+ " ,... More key-value pairs of the above form. For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
+ " accepted as second parameter.\n"
+ " ]\n"
"3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
"4. replaceable (boolean, optional, default=false) Marks this transaction as BIP125 replaceable.\n"
" Allows this transaction to be replaced by a transaction with higher fees. If provided, it is an error if explicit sequence numbers are incompatible.\n"
@@ -348,18 +354,29 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
"\"transaction\" (string) hex string of the transaction\n"
"\nExamples:\n"
- + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"")
- + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"data\\\":\\\"00010203\\\"}\"")
- + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"")
- + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"data\\\":\\\"00010203\\\"}\"")
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":0.01}]\"")
+ + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"address\\\":0.01}]\"")
+ + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"[{\\\"data\\\":\\\"00010203\\\"}]\"")
+ // clang-format on
);
+ }
- RPCTypeCheck(request.params, {UniValue::VARR, UniValue::VOBJ, UniValue::VNUM, UniValue::VBOOL}, true);
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValueType(), // ARR or OBJ, checked later
+ UniValue::VNUM,
+ UniValue::VBOOL
+ }, true
+ );
if (request.params[0].isNull() || request.params[1].isNull())
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, arguments 1 and 2 must be non-null");
UniValue inputs = request.params[0].get_array();
- UniValue sendTo = request.params[1].get_obj();
+ const bool outputs_is_obj = request.params[1].isObject();
+ UniValue outputs = outputs_is_obj ?
+ request.params[1].get_obj() :
+ request.params[1].get_array();
CMutableTransaction rawTx;
@@ -411,11 +428,24 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
}
std::set<CTxDestination> destinations;
- std::vector<std::string> addrList = sendTo.getKeys();
- for (const std::string& name_ : addrList) {
-
+ if (!outputs_is_obj) {
+ // 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];
+ if (!output.isObject()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair not an object as expected");
+ }
+ if (output.size() != 1) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, key-value pair must contain exactly one key");
+ }
+ outputs_dict.pushKVs(output);
+ }
+ outputs = std::move(outputs_dict);
+ }
+ for (const std::string& name_ : outputs.getKeys()) {
if (name_ == "data") {
- std::vector<unsigned char> data = ParseHexV(sendTo[name_].getValStr(),"Data");
+ std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
CTxOut out(0, CScript() << OP_RETURN << data);
rawTx.vout.push_back(out);
@@ -430,7 +460,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
}
CScript scriptPubKey = GetScriptForDestination(destination);
- CAmount nAmount = AmountFromValue(sendTo[name_]);
+ CAmount nAmount = AmountFromValue(outputs[name_]);
CTxOut out(nAmount, scriptPubKey);
rawTx.vout.push_back(out);