Add iswitness parameter to decode- and fundrawtransaction RPCs

This commit is contained in:
MeshCollider 2017-08-28 18:00:21 +12:00
parent 28485c783d
commit bbdbe805a2
6 changed files with 45 additions and 29 deletions

View File

@ -20,7 +20,7 @@ class UniValue;
// core_read.cpp // core_read.cpp
CScript ParseScript(const std::string& s); CScript ParseScript(const std::string& s);
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false); std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness = false); bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness = false, bool try_witness = true);
bool DecodeHexBlk(CBlock&, const std::string& strHexBlk); bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
uint256 ParseHashUV(const UniValue& v, const std::string& strName); uint256 ParseHashUV(const UniValue& v, const std::string& strName);
uint256 ParseHashStr(const std::string&, const std::string& strName); uint256 ParseHashStr(const std::string&, const std::string& strName);

View File

@ -108,39 +108,39 @@ bool CheckTxScriptsSanity(const CMutableTransaction& tx)
return true; return true;
} }
bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness) bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness, bool try_witness)
{ {
if (!IsHex(strHexTx)) { if (!IsHex(hex_tx)) {
return false; return false;
} }
std::vector<unsigned char> txData(ParseHex(strHexTx)); std::vector<unsigned char> txData(ParseHex(hex_tx));
if (fTryNoWitness) { if (try_no_witness) {
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
try { try {
ssData >> tx; ssData >> tx;
if (ssData.eof() && CheckTxScriptsSanity(tx)) { if (ssData.eof() && (!try_witness || CheckTxScriptsSanity(tx))) {
return true; return true;
} }
} } catch (const std::exception&) {
catch (const std::exception&) {
// Fall through. // Fall through.
} }
} }
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); if (try_witness) {
try { CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
ssData >> tx; try {
if (!ssData.empty()) { ssData >> tx;
return false; if (ssData.empty()) {
return true;
}
} catch (const std::exception&) {
// Fall through.
} }
} }
catch (const std::exception&) {
return false; return false;
}
return true;
} }
bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk) bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)

View File

@ -93,11 +93,13 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createrawtransaction", 1, "outputs" }, { "createrawtransaction", 1, "outputs" },
{ "createrawtransaction", 2, "locktime" }, { "createrawtransaction", 2, "locktime" },
{ "createrawtransaction", 3, "replaceable" }, { "createrawtransaction", 3, "replaceable" },
{ "decoderawtransaction", 1, "iswitness" },
{ "signrawtransaction", 1, "prevtxs" }, { "signrawtransaction", 1, "prevtxs" },
{ "signrawtransaction", 2, "privkeys" }, { "signrawtransaction", 2, "privkeys" },
{ "sendrawtransaction", 1, "allowhighfees" }, { "sendrawtransaction", 1, "allowhighfees" },
{ "combinerawtransaction", 0, "txs" }, { "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" }, { "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
{ "gettxout", 1, "n" }, { "gettxout", 1, "n" },
{ "gettxout", 2, "include_mempool" }, { "gettxout", 2, "include_mempool" },
{ "gettxoutproof", 0, "txids" }, { "gettxoutproof", 0, "txids" },

View File

@ -419,13 +419,15 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
UniValue decoderawtransaction(const JSONRPCRequest& request) UniValue decoderawtransaction(const JSONRPCRequest& request)
{ {
if (request.fHelp || request.params.size() != 1) if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
throw std::runtime_error( throw std::runtime_error(
"decoderawtransaction \"hexstring\"\n" "decoderawtransaction \"hexstring\" ( iswitness )\n"
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n" "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"
"\nArguments:\n" "\nArguments:\n"
"1. \"hexstring\" (string, required) The transaction hex string\n" "1. \"hexstring\" (string, required) The transaction hex string\n"
"2. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction\n"
" If iswitness is not present, heuristic tests will be used in decoding\n"
"\nResult:\n" "\nResult:\n"
"{\n" "{\n"
@ -473,12 +475,16 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
); );
LOCK(cs_main); LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VSTR}); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
CMutableTransaction mtx; CMutableTransaction mtx;
if (!DecodeHexTx(mtx, request.params[0].get_str(), true)) bool try_witness = request.params[1].isNull() ? true : request.params[1].get_bool();
bool try_no_witness = request.params[1].isNull() ? true : !request.params[1].get_bool();
if (!DecodeHexTx(mtx, request.params[0].get_str(), try_no_witness, try_witness)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
UniValue result(UniValue::VOBJ); UniValue result(UniValue::VOBJ);
TxToUniv(CTransaction(std::move(mtx)), uint256(), result, false); TxToUniv(CTransaction(std::move(mtx)), uint256(), result, false);
@ -966,7 +972,7 @@ static const CRPCCommand commands[] =
// --------------------- ------------------------ ----------------------- ---------- // --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose"} }, { "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose"} },
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} }, { "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} }, { "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} }, { "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} }, { "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} }, { "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },

View File

@ -65,7 +65,9 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams)
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "size").get_int(), 193); BOOST_CHECK_EQUAL(find_value(r.get_obj(), "size").get_int(), 193);
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1); BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1);
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0); BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0);
BOOST_CHECK_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" extra"), std::runtime_error); BOOST_CHECK_THROW(CallRPC(std::string("decoderawtransaction ")+rawtx+" extra"), std::runtime_error);
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false"));
BOOST_CHECK_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false extra"), std::runtime_error);
BOOST_CHECK_THROW(CallRPC("signrawtransaction"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("signrawtransaction"), std::runtime_error);
BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), std::runtime_error); BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), std::runtime_error);

View File

@ -2812,9 +2812,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
return NullUniValue; return NullUniValue;
} }
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
throw std::runtime_error( throw std::runtime_error(
"fundrawtransaction \"hexstring\" ( options )\n" "fundrawtransaction \"hexstring\" ( options iswitness )\n"
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
"This will not modify existing inputs, and will add at most one change output to the outputs.\n" "This will not modify existing inputs, and will add at most one change output to the outputs.\n"
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n" "No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
@ -2849,6 +2849,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
" \"CONSERVATIVE\"\n" " \"CONSERVATIVE\"\n"
" }\n" " }\n"
" for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n" " for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
"3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction \n"
" If iswitness is not present, heuristic tests will be used in decoding\n"
"\nResult:\n" "\nResult:\n"
"{\n" "{\n"
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
@ -2881,7 +2884,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
coinControl.fAllowWatchOnly = request.params[1].get_bool(); coinControl.fAllowWatchOnly = request.params[1].get_bool();
} }
else { else {
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ}); RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ, UniValue::VBOOL});
UniValue options = request.params[1]; UniValue options = request.params[1];
@ -2949,8 +2952,11 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
// parse hex string from parameter // parse hex string from parameter
CMutableTransaction tx; CMutableTransaction tx;
if (!DecodeHexTx(tx, request.params[0].get_str(), true)) bool try_witness = request.params[2].isNull() ? true : request.params[2].get_bool();
bool try_no_witness = request.params[2].isNull() ? true : !request.params[2].get_bool();
if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
if (tx.vout.size() == 0) if (tx.vout.size() == 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output"); throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
@ -3183,7 +3189,7 @@ extern UniValue importmulti(const JSONRPCRequest& request);
static const CRPCCommand commands[] = static const CRPCCommand commands[] =
{ // category name actor (function) argNames { // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ---------- // --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options"} }, { "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
{ "hidden", "resendwallettransactions", &resendwallettransactions, {} }, { "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} }, { "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} }, { "wallet", "abortrescan", &abortrescan, {} },