Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
},
},
},
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "a json array with outputs (key-value pairs).\n"
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "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"
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.",
Expand Down Expand Up @@ -1283,7 +1283,8 @@ UniValue createpsbt(const JSONRPCRequest& request)
},
},
},
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "a json array with outputs (key-value pairs).\n"
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "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"
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.",
{
Expand Down
10 changes: 9 additions & 1 deletion src/rpc/rawtransaction_util.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,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);
Expand All @@ -82,8 +81,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);
Expand Down
3 changes: 0 additions & 3 deletions src/test/rpc_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -124,9 +124,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);

Expand Down
3 changes: 2 additions & 1 deletion src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4041,7 +4041,8 @@ UniValue walletcreatefundedpsbt(const JSONRPCRequest& request)
},
},
},
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "a json array with outputs (key-value pairs).\n"
{"outputs", RPCArg::Type::ARR, RPCArg::Optional::NO, "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"
"For compatibility reasons, a dictionary, which holds the key-value pairs directly, is also\n"
" accepted as second parameter.",
{
Expand Down
6 changes: 3 additions & 3 deletions src/wallet/wallettool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace WalletTool {
static void WalletToolReleaseWallet(CWallet* wallet)
{
wallet->WalletLogPrintf("Releasing wallet\n");
wallet->Flush();
wallet->Flush(true);
delete wallet;
}

Expand Down Expand Up @@ -114,7 +114,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
std::shared_ptr<CWallet> wallet_instance = CreateWallet(name, path);
if (wallet_instance) {
WalletShowInfo(wallet_instance.get());
wallet_instance->Flush();
wallet_instance->Flush(true);
}
} else if (command == "info") {
if (!fs::exists(path)) {
Expand All @@ -129,7 +129,7 @@ bool ExecuteWalletToolFunc(const std::string& command, const std::string& name)
std::shared_ptr<CWallet> wallet_instance = LoadWallet(name, path);
if (!wallet_instance) return false;
WalletShowInfo(wallet_instance.get());
wallet_instance->Flush();
wallet_instance->Flush(true);
} else {
tfm::format(std::cerr, "Invalid command: %s\n", command);
return false;
Expand Down
13 changes: 4 additions & 9 deletions test/functional/rpc_rawtransaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ def run_test(self):
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']])

Expand All @@ -133,19 +135,12 @@ def run_test(self):
tx.serialize().hex(),
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(
tx.serialize().hex(),
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(
tx.serialize().hex(),
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'}]),
)

self.log.info('sendrawtransaction with missing input')
Expand Down