rpc: Avoid creating non-standard raw transactions
Github-Pull: #14890
Rebased-From: fa4c8679ed
This commit is contained in:
parent
ef70f9b52b
commit
46c162df47
4 changed files with 19 additions and 16 deletions
|
@ -396,7 +396,6 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
||||||
rawTx.vin.push_back(in);
|
rawTx.vin.push_back(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<CTxDestination> destinations;
|
|
||||||
if (!outputs_is_obj) {
|
if (!outputs_is_obj) {
|
||||||
// Translate array of key-value pairs into dict
|
// Translate array of key-value pairs into dict
|
||||||
UniValue outputs_dict = UniValue(UniValue::VOBJ);
|
UniValue outputs_dict = UniValue(UniValue::VOBJ);
|
||||||
|
@ -412,8 +411,17 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal
|
||||||
}
|
}
|
||||||
outputs = std::move(outputs_dict);
|
outputs = std::move(outputs_dict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Duplicate checking
|
||||||
|
std::set<CTxDestination> destinations;
|
||||||
|
bool has_data{false};
|
||||||
|
|
||||||
for (const std::string& name_ : outputs.getKeys()) {
|
for (const std::string& name_ : outputs.getKeys()) {
|
||||||
if (name_ == "data") {
|
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");
|
std::vector<unsigned char> data = ParseHexV(outputs[name_].getValStr(), "Data");
|
||||||
|
|
||||||
CTxOut out(0, CScript() << OP_RETURN << data);
|
CTxOut out(0, CScript() << OP_RETURN << data);
|
||||||
|
@ -465,7 +473,8 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
|
||||||
" } \n"
|
" } \n"
|
||||||
" ,...\n"
|
" ,...\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"
|
||||||
" {\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"
|
" \"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"
|
" ,...\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"
|
||||||
" {\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"
|
" \"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"
|
||||||
|
|
|
@ -122,9 +122,6 @@ BOOST_AUTO_TEST_CASE(rpc_createraw_op_return)
|
||||||
{
|
{
|
||||||
BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"68656c6c6f776f726c64\"}"));
|
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)
|
// Key not "data" (bad address)
|
||||||
BOOST_CHECK_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"somedata\":\"68656c6c6f776f726c64\"}"), std::runtime_error);
|
BOOST_CHECK_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"somedata\":\"68656c6c6f776f726c64\"}"), std::runtime_error);
|
||||||
|
|
||||||
|
|
|
@ -4680,7 +4680,8 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
|
||||||
" } \n"
|
" } \n"
|
||||||
" ,...\n"
|
" ,...\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"
|
||||||
" {\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"
|
" \"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"
|
||||||
|
|
|
@ -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(-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, [], 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, 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 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']])
|
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()),
|
bytes_to_hex_str(tx.serialize()),
|
||||||
self.nodes[2].createrawtransaction(inputs=[{'txid': txid, 'vout': 9}], outputs=[{address: 99}, {address2: 99}]),
|
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
|
# 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(len(tx.vout), 3)
|
||||||
assert_equal(
|
assert_equal(
|
||||||
bytes_to_hex_str(tx.serialize()),
|
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"]:
|
for type in ["bech32", "p2sh-segwit", "legacy"]:
|
||||||
|
|
Loading…
Reference in a new issue