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)
This commit is contained in:
commit
6bb28058d3
7 changed files with 111 additions and 16 deletions
|
@ -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 = \
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(address.Get());
|
||||
CAmount nAmount = AmountFromValue(sendTo[name_]);
|
||||
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_);
|
||||
|
||||
CTxOut out(nAmount, scriptPubKey);
|
||||
rawTx.vout.push_back(out);
|
||||
if (setAddress.count(address))
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_);
|
||||
setAddress.insert(address);
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(address.Get());
|
||||
CAmount nAmount = AmountFromValue(sendTo[name_]);
|
||||
|
||||
CTxOut out(nAmount, scriptPubKey);
|
||||
rawTx.vout.push_back(out);
|
||||
}
|
||||
}
|
||||
|
||||
return EncodeHexTx(rawTx);
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
]
|
||||
|
|
1
src/test/data/txcreatedata1.hex
Normal file
1
src/test/data/txcreatedata1.hex
Normal file
|
@ -0,0 +1 @@
|
|||
01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0084d71700000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
|
1
src/test/data/txcreatedata2.hex
Normal file
1
src/test/data/txcreatedata2.hex
Normal file
|
@ -0,0 +1 @@
|
|||
01000000011f5c38dfcf6f1a5f5a87c416076d392c87e6d41970d5ad5e477a02d66bde97580000000000ffffffff0280a81201000000001976a9141fc11f39be1729bf973a7ab6a615ca4729d6457488ac0000000000000000526a4c4f54686973204f505f52455455524e207472616e73616374696f6e206f7574707574207761732063726561746564206279206d6f646966696564206372656174657261777472616e73616374696f6e2e00000000
|
|
@ -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");
|
||||
|
|
Loading…
Reference in a new issue