diff --git a/src/qt/qrc_bitcoin_build.qrc b/src/qt/qrc_bitcoin_build.qrc
new file mode 100644
index 0000000000..1c9c9ba90a
--- /dev/null
+++ b/src/qt/qrc_bitcoin_build.qrc
@@ -0,0 +1,96 @@
+
+
+ res/rendered_icons/bitcoin1024.png
+ res/icons/address-book.png
+ res/icons/quit.png
+ res/icons/send.png
+ res/icons/connect0.png
+ res/icons/connect1.png
+ res/icons/connect2.png
+ res/icons/connect3.png
+ res/icons/connect4.png
+ res/icons/transaction0.png
+ res/icons/transaction2.png
+ res/icons/transaction_conflicted.png
+ res/icons/clock1.png
+ res/icons/clock2.png
+ res/icons/clock3.png
+ res/icons/clock4.png
+ res/icons/clock5.png
+ res/icons/eye.png
+ res/icons/eye_minus.png
+ res/icons/eye_plus.png
+ res/icons/configure.png
+ res/icons/receive.png
+ res/icons/editpaste.png
+ res/icons/editcopy.png
+ res/icons/add.png
+ res/icons/edit.png
+ res/icons/history.png
+ res/icons/overview.png
+ res/icons/export.png
+ res/icons/synced.png
+ res/icons/remove.png
+ res/icons/tx_mined.png
+ res/icons/tx_input.png
+ res/icons/tx_output.png
+ res/icons/tx_inout.png
+ res/icons/lock_closed.png
+ res/icons/lock_open.png
+ res/icons/key.png
+ res/icons/filesave.png
+ res/icons/debugwindow.png
+ res/icons/open.png
+ res/icons/info.png
+ res/rendered_icons/about.png
+ res/icons/about_qt.png
+ res/icons/verify.png
+ res/icons/warning.png
+ res/icons/fontbigger.png
+ res/icons/fontsmaller.png
+ res/icons/chevron.png
+ res/icons/transaction_abandoned.png
+ res/icons/hd_enabled.png
+ res/icons/hd_disabled.png
+ res/icons/network_disabled.png
+ res/icons/proxy.png
+
+
+ res/movies/spinner-000.png
+ res/movies/spinner-001.png
+ res/movies/spinner-002.png
+ res/movies/spinner-003.png
+ res/movies/spinner-004.png
+ res/movies/spinner-005.png
+ res/movies/spinner-006.png
+ res/movies/spinner-007.png
+ res/movies/spinner-008.png
+ res/movies/spinner-009.png
+ res/movies/spinner-010.png
+ res/movies/spinner-011.png
+ res/movies/spinner-012.png
+ res/movies/spinner-013.png
+ res/movies/spinner-014.png
+ res/movies/spinner-015.png
+ res/movies/spinner-016.png
+ res/movies/spinner-017.png
+ res/movies/spinner-018.png
+ res/movies/spinner-019.png
+ res/movies/spinner-020.png
+ res/movies/spinner-021.png
+ res/movies/spinner-022.png
+ res/movies/spinner-023.png
+ res/movies/spinner-024.png
+ res/movies/spinner-025.png
+ res/movies/spinner-026.png
+ res/movies/spinner-027.png
+ res/movies/spinner-028.png
+ res/movies/spinner-029.png
+ res/movies/spinner-030.png
+ res/movies/spinner-031.png
+ res/movies/spinner-032.png
+ res/movies/spinner-033.png
+ res/movies/spinner-034.png
+ res/movies/spinner-035.png
+
+
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 1d9dd0ffab..127482829e 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -789,6 +789,125 @@ static UniValue createrawtransaction(const JSONRPCRequest& request)
return EncodeHexTx(CTransaction(rawTx));
}
+UniValue createrawtxoutputs(const JSONRPCRequest& request)
+{
+ if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
+ throw std::runtime_error(
+ "createrawtxoutputs"
+ "\nCreate a transaction spending the given inputs and creating specified outputs.\n"
+ "Outputs are explicitly sepcified as an array, including the asset ID.\n"
+ "Returns hex-encoded raw transaction.\n"
+ "Note that the transaction's inputs are not signed, and\n"
+ "it is not stored in the wallet or transmitted to the network.\n"
+
+ "\nArguments:\n"
+ "1. \"inputs\" (array, required) A json array of json objects\n"
+ " [\n"
+ " {\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\" (array, required) a json array of json objects for each output\n"
+ " {\n"
+ " \"address\": \"address\", (numeric or string, optional) pay to a specified address, or if \"fee\" create a fee output\n"
+ " \"amount\": x.xxxx, (numeric, required) value is the " + CURRENCY_UNIT + " amount of the output\n"
+ " \"data\": \"hex\" (string, optional) Output is OP_RETURN with the hex encoded data (alternative to address)\n"
+ " \"asset\": \"assetid\" (string, required) The value is the asset ID of the outputs\n"
+ " }\n"
+ " ,...\n"
+ " ]\n"
+ "3. locktime (numeric, optional, default=0) Raw locktime. Non-0 value also locktime-activates inputs\n"
+
+ "\nResult:\n"
+ "\"transaction\" (string) hex string of the transaction\n"
+
+ "\nExamples:\n"
+ + HelpExampleCli("createrawtxoutputs", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":\\\"myaddress\\\",\\\"amount\\\":2.34231,\\\"asset\\\":\\\"assetid\\\"}]\"")
+ + HelpExampleCli("createrawtxoutputs", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"data\\\":\\\"da3c69bc\\\",\\\"amount\\\":2.34231,\\\"asset\\\":\\\"assetid\\\"}]\"")
+ + HelpExampleCli("createrawtxoutputs", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"[{\\\"address\\\":\\\"myaddress\\\",\\\"amount\\\":2.34231,\\\"asset\\\":\\\"assetid\\\"},{\\\"address\\\":\\\"fee\\\",\\\"amount\\\":0.01,\\\"asset\\\":\\\"assetid\\\"}]\"")
+ );
+ RPCTypeCheck(request.params, {
+ UniValue::VARR,
+ UniValue::VARR,
+ UniValue::VNUM,
+ }, 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 outputs = request.params[1].get_array();
+ CMutableTransaction rawTx;
+ if (request.params.size() > 2 && !request.params[2].isNull()) {
+ int64_t nLockTime = request.params[2].get_int64();
+ if (nLockTime < 0 || nLockTime > std::numeric_limits::max())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, locktime out of range");
+ rawTx.nLockTime = nLockTime;
+ }
+
+ UniValue assets;
+
+ for (unsigned int idx = 0; idx < inputs.size(); idx++) {
+ UniValue const &input = inputs[idx];
+ UniValue const &o = input.get_obj();
+ uint256 txid = ParseHashO(o, "txid");
+ UniValue const &vout_v = find_value(o, "vout");
+ if (!vout_v.isNum())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
+ int nOutput = vout_v.get_int();
+ if (nOutput < 0)
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
+ uint32_t nSequence = (rawTx.nLockTime ? std::numeric_limits::max() - 1 : std::numeric_limits::max());
+ // set the sequence number if passed in the parameters object
+ const UniValue& sequenceObj = find_value(o, "sequence");
+ if (sequenceObj.isNum()) {
+ int64_t seqNr64 = sequenceObj.get_int64();
+ if (seqNr64 < 0 || seqNr64 > std::numeric_limits::max())
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, sequence number is out of range");
+ else
+ nSequence = (uint32_t)seqNr64;
+ }
+ CTxIn in(COutPoint(txid, nOutput), CScript(), nSequence);
+ rawTx.vin.push_back(in);
+ }
+
+ for (unsigned int idx = 0; idx < outputs.size(); idx++) {
+ UniValue const &output = outputs[idx];
+ UniValue const &o = output.get_obj();
+ uint256 assetid = ParseHashO(o, "asset");
+ CAsset asset(assetid);
+ CAmount nAmount = AmountFromValue(find_value(o, "amount"));
+
+ const UniValue& addressObj = find_value(o, "address");
+
+ if(addressObj.isStr()) {
+ if (addressObj.getValStr() == "fee") {
+ CTxOut out(asset, nAmount, CScript());
+ rawTx.vout.push_back(out);
+ } else {
+ CTxDestination destination = DecodeDestination(addressObj.getValStr());
+ if (!IsValidDestination(destination)) {
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Bitcoin address: ") + addressObj.getValStr());
+ }
+ CScript scriptPubKey = GetScriptForDestination(destination);
+ CTxOut out(asset, nAmount, scriptPubKey);
+ rawTx.vout.push_back(out);
+ }
+ } else {
+ const UniValue& dataObj = find_value(o, "data");
+ if(!dataObj.isStr()) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Output object without address or data key");
+ }
+ std::vector data = ParseHexV(dataObj.getValStr(),"Data");
+ CTxOut out(asset, nAmount, CScript() << OP_RETURN << data);
+ rawTx.vout.push_back(out);
+ }
+ }
+ return EncodeHexTx(CTransaction(rawTx));
+}
+
static UniValue decoderawtransaction(const JSONRPCRequest& request)
{
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
@@ -3139,6 +3258,7 @@ static const CRPCCommand commands[] =
// --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable", "output_assets"} },
+ { "rawtransactions", "createrawtxoutputs", &createrawtxoutputs, {"inputs","outputs","locktime"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
diff --git a/test/functional/rpc_createrawtxoutputs.py b/test/functional/rpc_createrawtxoutputs.py
new file mode 100755
index 0000000000..7afd516467
--- /dev/null
+++ b/test/functional/rpc_createrawtxoutputs.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# Copyright (c) 2015-2018 The Bitcoin Core developers
+# Distributed under the MIT software license, see the accompanying
+# file COPYING or http://www.opensource.org/licenses/mit-license.php.
+"""Test transaction signing using the signrawtransaction* RPCs."""
+
+from test_framework.test_framework import BitcoinTestFramework
+import decimal
+
+class RpcCreateRawTxOutputsTest(BitcoinTestFramework):
+ def set_test_params(self):
+ self.setup_clean_chain = True
+ self.num_nodes = 3
+
+ def skip_test_if_missing_module(self):
+ self.skip_if_no_wallet()
+
+ def run_test(self):
+ node0,node1,node2 = self.nodes
+
+ # 50 BTC each, rest will be 25 BTC each
+ node0.generate(149)
+ self.sync_all()
+
+ self.do_createraw()
+
+ def do_createraw(self):
+ node0,node1,node2 = self.nodes
+
+ # test createrawtxoutputs RPC
+ addr1 = node2.getnewaddress()
+ addr2 = node2.getnewaddress()
+ issue1 = node2.issueasset(10.0,0,False)
+ issue2 = node2.issueasset(10.0,0,False)
+ self.sync_all()
+ node0.generate(10)
+ self.sync_all()
+
+ rawissue1 = node2.getrawtransaction(issue1["txid"],True)
+ for vout in rawissue1["vout"]:
+ if vout["asset"] == issue1["asset"]: vout1 = vout["n"]
+ rawissue2 = node2.getrawtransaction(issue2["txid"],True)
+ for vout in rawissue2["vout"]:
+ if vout["asset"] == issue2["asset"]: vout2 = vout["n"]
+
+ inputs = []
+ outputs = []
+ inputs.append({"txid":issue1["txid"],"vout":vout1})
+ inputs.append({"txid":issue2["txid"],"vout":vout2})
+ outputs.append({"address":addr1,"amount":10.0,"asset":issue1["asset"]})
+ outputs.append({"address":addr1,"amount":5.0,"asset":issue2["asset"]})
+ outputs.append({"address":addr2,"amount":4.99,"asset":issue2["asset"]})
+ outputs.append({"address":"fee","amount":0.01,"asset":issue2["asset"]})
+ outputs.append({"data":"deadbeef","amount":0.0,"asset":issue2["asset"]})
+
+ # create a transaction with same address outputs
+ node2.createrawtxoutputs(inputs,outputs)
+ node2.signrawtransaction(rawtx)
+ node2.sendrawtransaction(signtx["hex"])
+
+ self.sync_all()
+ node0.generate(1)
+ self.sync_all()
+
+ txdec = node2.getrawtransaction(sendtx,True)
+
+ assert txdec["confirmations"] == 1
+ assert len(txdec["vout"]) == 5
+
+
+if __name__ == '__main__':
+ RpcCreateRawTxOutputsTest().main()
diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py
index 9f5712a62a..ad9d825f42 100755
--- a/test/functional/test_runner.py
+++ b/test/functional/test_runner.py
@@ -169,6 +169,7 @@
'rpc_invalidateblock.py',
'feature_rbf.py',
'mempool_packages.py',
+ 'rpc_createrawtxoutputs.py',
'rpc_createmultisig.py',
# ELEMENTS: no versionbits in use
#'feature_versionbits_warning.py',