aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcoFalke <falke.marco@gmail.com>2018-12-24 13:06:06 +0100
committerMarcoFalke <falke.marco@gmail.com>2018-12-24 13:06:32 +0100
commite11856515e7c1b7ca4b67041ba51b117df173a11 (patch)
tree40f4fc6e29178dd724640995bc1f9a7ce09d2d32
parenta057cc08fddd6503c682a2110092fce259346eae (diff)
parent46c162df478f3b71bef8217f214ccf202576b733 (diff)
Merge #14893: 0.17 [Backport 14890] rpc: Avoid creating non-standard raw transactions
46c162df47 rpc: Avoid creating non-standard raw transactions (MarcoFalke) Pull request description: Tree-SHA512: f34678637c8b6559e5c0c2790b682af562479239b92be96e0d41806bade136866f9748487a021eb8c62b6a5027b0a1a2cbdee930243eac93edabef60cbd54eac
-rw-r--r--src/rpc/rawtransaction.cpp16
-rw-r--r--src/test/rpc_tests.cpp3
-rw-r--r--src/wallet/rpcwallet.cpp3
-rwxr-xr-xtest/functional/rpc_rawtransaction.py13
4 files changed, 19 insertions, 16 deletions
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 0925b1c0cf..391e744c90 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -396,7 +396,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
rawTx.vin.push_back(in);
}
- std::set<CTxDestination> destinations;
if (!outputs_is_obj) {
// Translate array of key-value pairs into dict
UniValue outputs_dict = UniValue(UniValue::VOBJ);
@@ -412,8 +411,17 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
}
outputs = std::move(outputs_dict);
}
+
+ // Duplicate checking
+ std::set<CTxDestination> destinations;
+ bool has_data{false};
+
for (const std::string& name_ : outputs.getKeys()) {
if (name_ == "data") {
+ if (has_data) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, duplicate key: data");
+ }
+ has_data = true;
std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
CTxOut out(0, CScript() << OP_RETURN << data);
@@ -465,7 +473,8 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
" } \n"
" ,...\n"
" ]\n"
- "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ "2. \"outputs\" (array, required) a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ "That is, each address can only appear once and there can only be one 'data' object.\n"
" [\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"
@@ -1690,7 +1699,8 @@ UniValue createpsbt(const JSONRPCRequest& request)
" } \n"
" ,...\n"
" ]\n"
- "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ "2. \"outputs\" (array, required) a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ "That is, each address can only appear once and there can only be one 'data' object.\n"
" [\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"
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index a49796d6f4..943bbf3137 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -122,9 +122,6 @@ BOOST_AUTO_TEST_CASE(rpc_createraw_op_return)
{
BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"68656c6c6f776f726c64\"}"));
- // Allow more than one data transaction output
- BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"68656c6c6f776f726c64\",\"data\":\"68656c6c6f776f726c64\"}"));
-
// Key not "data" (bad address)
BOOST_CHECK_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"somedata\":\"68656c6c6f776f726c64\"}"), std::runtime_error);
diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp
index 193da76551..e02171ec40 100644
--- a/src/wallet/rpcwallet.cpp
+++ b/src/wallet/rpcwallet.cpp
@@ -4680,7 +4680,8 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
" } \n"
" ,...\n"
" ]\n"
- "2. \"outputs\" (array, required) a json array with outputs (key-value pairs)\n"
+ "2. \"outputs\" (array, required) a json array with outputs (key-value pairs), where none of the keys are duplicated.\n"
+ "That is, each address can only appear once and there can only be one 'data' object.\n"
" [\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"
diff --git a/test/functional/rpc_rawtransaction.py b/test/functional/rpc_rawtransaction.py
index d86b546c7d..c62e04fe14 100755
--- a/test/functional/rpc_rawtransaction.py
+++ b/test/functional/rpc_rawtransaction.py
@@ -99,6 +99,8 @@ class RawTransactionsTest(BitcoinTestFramework):
assert_raises_rpc_error(-3, "Amount out of range", self.nodes[0].createrawtransaction, [], {address: -1})
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], multidict([(address, 1), (address, 1)]))
assert_raises_rpc_error(-8, "Invalid parameter, duplicated address: %s" % address, self.nodes[0].createrawtransaction, [], [{address: 1}, {address: 1}])
+ assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], [{"data": 'aa'}, {"data": "bb"}])
+ assert_raises_rpc_error(-8, "Invalid parameter, duplicate key: data", self.nodes[0].createrawtransaction, [], multidict([("data", 'aa'), ("data", "bb")]))
assert_raises_rpc_error(-8, "Invalid parameter, key-value pair must contain exactly one key", self.nodes[0].createrawtransaction, [], [{'a': 1, 'b': 2}])
assert_raises_rpc_error(-8, "Invalid parameter, key-value pair not an object as expected", self.nodes[0].createrawtransaction, [], [['key-value pair1'], ['2']])
@@ -126,19 +128,12 @@ class RawTransactionsTest(BitcoinTestFramework):
bytes_to_hex_str(tx.serialize()),
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}]),
)
- # Two data outputs
- tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([('data', '99'), ('data', '99')])))))
- assert_equal(len(tx.vout), 2)
- assert_equal(
- bytes_to_hex_str(tx.serialize()),
- self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{'data': '99'}, {'data': '99'}]),
- )
# Multiple mixed outputs
- tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), ('data', '99'), ('data', '99')])))))
+ tx.deserialize(BytesIO(hex_str_to_bytes(self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=multidict([(address, 99), (address2, 99), ('data', '99')])))))
assert_equal(len(tx.vout), 3)
assert_equal(
bytes_to_hex_str(tx.serialize()),
- self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {'data': '99'}, {'data': '99'}]),
+ self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}, {'data': '99'}]),
)
for type in ["bech32", "p2sh-segwit", "legacy"]: