aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2015-08-10 17:51:03 +0200
committerWladimir J. van der Laan <laanwj@gmail.com>2015-08-10 17:53:07 +0200
commit6bb28058d3ede4ff0b8e6d5315f0c2386f3e1131 (patch)
tree54b61c8244243ea3673c814e30a712c66706f51c
parent0201a79299f782193897d3ba2d7a2904fa4c11ea (diff)
parent627468d2ea1f4d5dea9561fe77aaf53f3a83b4d2 (diff)
Merge pull request #6346
627468d Add support for data-based outputs (OP_RETURN) to bitcoin-tx. (Pavel Janík) d707853 Add OP_RETURN support in createrawtransaction RPC call, add tests. (Pavel Janík)
-rw-r--r--src/Makefile.test.include2
-rw-r--r--src/bitcoin-tx.cpp32
-rw-r--r--src/rpcrawtransaction.cpp43
-rw-r--r--src/test/data/bitcoin-util-test.json30
-rw-r--r--src/test/data/txcreatedata1.hex1
-rw-r--r--src/test/data/txcreatedata2.hex1
-rw-r--r--src/test/rpc_tests.cpp18
7 files changed, 111 insertions, 16 deletions
diff --git a/src/Makefile.test.include b/src/Makefile.test.include
index f9384a09a4..8289198959 100644
--- a/src/Makefile.test.include
+++ b/src/Makefile.test.include
@@ -15,6 +15,8 @@ EXTRA_DIST += \
test/data/tx394b54bb.hex \
test/data/txcreate1.hex \
test/data/txcreate2.hex \
+ test/data/txcreatedata1.hex \
+ test/data/txcreatedata2.hex \
test/data/txcreatesign.hex
JSON_TEST_FILES = \
diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp
index e389d51a73..97a073174d 100644
--- a/src/bitcoin-tx.cpp
+++ b/src/bitcoin-tx.cpp
@@ -70,6 +70,7 @@ static bool AppInitRawTx(int argc, char* argv[])
strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
+ strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX"));
strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX"));
strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
_("This command requires JSON registers:") +
@@ -231,6 +232,35 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
tx.vout.push_back(txout);
}
+static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput)
+{
+ CAmount value = 0;
+
+ // separate [VALUE:]DATA in string
+ size_t pos = strInput.find(':');
+
+ if (pos==0)
+ throw runtime_error("TX output value not specified");
+
+ if (pos != string::npos) {
+ // extract and validate VALUE
+ string strValue = strInput.substr(0, pos);
+ if (!ParseMoney(strValue, value))
+ throw runtime_error("invalid TX output value");
+ }
+
+ // extract and validate DATA
+ string strData = strInput.substr(pos + 1, string::npos);
+
+ if (!IsHex(strData))
+ throw runtime_error("invalid TX output data");
+
+ std::vector<unsigned char> data = ParseHex(strData);
+
+ CTxOut txout(value, CScript() << OP_RETURN << data);
+ tx.vout.push_back(txout);
+}
+
static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
{
// separate VALUE:SCRIPT in string
@@ -470,6 +500,8 @@ static void MutateTx(CMutableTransaction& tx, const string& command,
MutateTxDelOutput(tx, commandVal);
else if (command == "outaddr")
MutateTxAddOutAddr(tx, commandVal);
+ else if (command == "outdata")
+ MutateTxAddOutData(tx, commandVal);
else if (command == "outscript")
MutateTxAddOutScript(tx, commandVal);
diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp
index a3246b41fb..9eeca5b7d9 100644
--- a/src/rpcrawtransaction.cpp
+++ b/src/rpcrawtransaction.cpp
@@ -318,8 +318,9 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
{
if (fHelp || params.size() != 2)
throw runtime_error(
- "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...}\n"
- "\nCreate a transaction spending the given inputs and sending to the given addresses.\n"
+ "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,\"data\":\"hex\",...}\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"
"Note that the transaction's inputs are not signed, and\n"
"it is not stored in the wallet or transmitted to the network.\n"
@@ -328,23 +329,25 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
"1. \"transactions\" (string, 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"
" }\n"
" ,...\n"
" ]\n"
- "2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n"
+ "2. \"outputs\" (string, required) a json object with outputs\n"
" {\n"
" \"address\": x.xxx (numeric, required) The key is the bitcoin address, the value is the " + CURRENCY_UNIT + " amount\n"
- " ,...\n"
+ " \"data\": \"hex\", (string, required) The key is \"data\", the value is hex encoded data\n"
+ " ...\n"
" }\n"
-
"\nResult:\n"
"\"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\\\"}\"")
);
LOCK(cs_main);
@@ -375,19 +378,27 @@ UniValue createrawtransaction(const UniValue& params, bool fHelp)
set<CBitcoinAddress> setAddress;
vector<string> addrList = sendTo.getKeys();
BOOST_FOREACH(const string& name_, addrList) {
- CBitcoinAddress address(name_);
- if (!address.IsValid())
- throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_);
- if (setAddress.count(address))
- throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_);
- setAddress.insert(address);
+ if (name_ == "data") {
+ std::vector<unsigned char> data = ParseHexV(sendTo[name_].getValStr(),"Data");
+
+ CTxOut out(0, CScript() << OP_RETURN << data);
+ rawTx.vout.push_back(out);
+ } else {
+ CBitcoinAddress address(name_);
+ if (!address.IsValid())
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+name_);
- CScript scriptPubKey = GetScriptForDestination(address.Get());
- CAmount nAmount = AmountFromValue(sendTo[name_]);
+ if (setAddress.count(address))
+ throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_);
+ setAddress.insert(address);
- CTxOut out(nAmount, scriptPubKey);
- rawTx.vout.push_back(out);
+ CScript scriptPubKey = GetScriptForDestination(address.Get());
+ CAmount nAmount = AmountFromValue(sendTo[name_]);
+
+ CTxOut out(nAmount, scriptPubKey);
+ rawTx.vout.push_back(out);
+ }
}
return EncodeHexTx(rawTx);
diff --git a/src/test/data/bitcoin-util-test.json b/src/test/data/bitcoin-util-test.json
index afd35af503..3bf80ca434 100644
--- a/src/test/data/bitcoin-util-test.json
+++ b/src/test/data/bitcoin-util-test.json
@@ -56,5 +56,35 @@
"sign=ALL",
"outaddr=0.001:193P6LtvS4nCnkDvM9uXn1gsSRqh4aDAz7"],
"output_cmp": "txcreatesign.hex"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
+ "outdata=4:badhexdata"],
+ "return_code": 1
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
+ "outdata=badhexdata"],
+ "return_code": 1
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
+ "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o",
+ "outdata=4:54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"],
+ "output_cmp": "txcreatedata1.hex"
+ },
+ { "exec": "./bitcoin-tx",
+ "args":
+ ["-create",
+ "in=5897de6bd6027a475eadd57019d4e6872c396d0716c4875a5f1a6fcfdf385c1f:0",
+ "outaddr=0.18:13tuJJDR2RgArmgfv6JScSdreahzgc4T6o",
+ "outdata=54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e"],
+ "output_cmp": "txcreatedata2.hex"
}
]
diff --git a/src/test/data/txcreatedata1.hex b/src/test/data/txcreatedata1.hex
new file mode 100644
index 0000000000..eccc7604e6
--- /dev/null
+++ b/src/test/data/txcreatedata1.hex
@@ -0,0 +1 @@
+01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d71700000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
diff --git a/src/test/data/txcreatedata2.hex b/src/test/data/txcreatedata2.hex
new file mode 100644
index 0000000000..3c7644c297
--- /dev/null
+++ b/src/test/data/txcreatedata2.hex
@@ -0,0 +1 @@
+01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
diff --git a/src/test/rpc_tests.cpp b/src/test/rpc_tests.cpp
index 7946b02855..a65572e6f6 100644
--- a/src/test/rpc_tests.cpp
+++ b/src/test/rpc_tests.cpp
@@ -110,6 +110,24 @@ BOOST_AUTO_TEST_CASE(rpc_rawsign)
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true);
}
+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\"}"), runtime_error);
+
+ // Bad hex encoding of data output
+ BOOST_CHECK_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"12345\"}"), runtime_error);
+ BOOST_CHECK_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"12345g\"}"), runtime_error);
+
+ // Data 81 bytes long
+ BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [{\"txid\":\"a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed\",\"vout\":0}] {\"data\":\"010203040506070809101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081\"}"));
+}
+
BOOST_AUTO_TEST_CASE(rpc_format_monetary_values)
{
BOOST_CHECK(ValueFromAmount(0LL).write() == "0.00000000");