Skip to content

Commit

Permalink
Merge #11178: Add iswitness parameter to decode- and fundrawtransacti…
Browse files Browse the repository at this point in the history
…on RPCs

6f39ac0 Add test for decoderawtransaction bool (MeshCollider)
bbdbe80 Add iswitness parameter to decode- and fundrawtransaction RPCs (MeshCollider)

Pull request description:

  Suggested in #10481 (comment), this adds the option to explicitly choose whether a serialized transaction should be decoded as a witness or non-witness transaction rather than relying on the heuristic checks in #10481. The parameter defaults to relying on #10481 if not included, but it overrides that if included.

Tree-SHA512: d4846a5bb7d64dc19c516445488b00af329fc1f4181d9dfdf9f2382a086568edc98250a4ac7594e24a1bc231dfdee53c699b12c8380c355b920a67cc6770b7a9
  • Loading branch information
laanwj committed Dec 19, 2017
2 parents 483bb67 + 6f39ac0 commit fee0370
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 29 deletions.
2 changes: 1 addition & 1 deletion src/core_io.h
Expand Up @@ -20,7 +20,7 @@ class UniValue;
// core_read.cpp
CScript ParseScript(const std::string& s);
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);
uint256 ParseHashUV(const UniValue& v, const std::string& strName);
uint256 ParseHashStr(const std::string&, const std::string& strName);
Expand Down
34 changes: 17 additions & 17 deletions src/core_read.cpp
Expand Up @@ -108,39 +108,39 @@ bool CheckTxScriptsSanity(const CMutableTransaction& tx)
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;
}

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);
try {
ssData >> tx;
if (ssData.eof() && CheckTxScriptsSanity(tx)) {
if (ssData.eof() && (!try_witness || CheckTxScriptsSanity(tx))) {
return true;
}
}
catch (const std::exception&) {
} catch (const std::exception&) {
// Fall through.
}
}

CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
try {
ssData >> tx;
if (!ssData.empty()) {
return false;
if (try_witness) {
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
try {
ssData >> tx;
if (ssData.empty()) {
return true;
}
} catch (const std::exception&) {
// Fall through.
}
}
catch (const std::exception&) {
return false;
}

return true;

return false;
}

bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/client.cpp
Expand Up @@ -91,11 +91,13 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "createrawtransaction", 1, "outputs" },
{ "createrawtransaction", 2, "locktime" },
{ "createrawtransaction", 3, "replaceable" },
{ "decoderawtransaction", 1, "iswitness" },
{ "signrawtransaction", 1, "prevtxs" },
{ "signrawtransaction", 2, "privkeys" },
{ "sendrawtransaction", 1, "allowhighfees" },
{ "combinerawtransaction", 0, "txs" },
{ "fundrawtransaction", 1, "options" },
{ "fundrawtransaction", 2, "iswitness" },
{ "gettxout", 1, "n" },
{ "gettxout", 2, "include_mempool" },
{ "gettxoutproof", 0, "txids" },
Expand Down
16 changes: 11 additions & 5 deletions src/rpc/rawtransaction.cpp
Expand Up @@ -441,13 +441,15 @@ UniValue createrawtransaction(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(
"decoderawtransaction \"hexstring\"\n"
"decoderawtransaction \"hexstring\" ( iswitness )\n"
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"

"\nArguments:\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"
"{\n"
Expand Down Expand Up @@ -495,12 +497,16 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
);

LOCK(cs_main);
RPCTypeCheck(request.params, {UniValue::VSTR});
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});

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");
}

UniValue result(UniValue::VOBJ);
TxToUniv(CTransaction(std::move(mtx)), uint256(), result, false);
Expand Down Expand Up @@ -1016,7 +1022,7 @@ static const CRPCCommand commands[] =
// --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} },
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
Expand Down
4 changes: 3 additions & 1 deletion src/test/rpc_tests.cpp
Expand Up @@ -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(), "version").get_int(), 1);
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 null"), std::runtime_error);
Expand Down
16 changes: 11 additions & 5 deletions src/wallet/rpcwallet.cpp
Expand Up @@ -2982,9 +2982,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
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(
"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"
"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"
Expand Down Expand Up @@ -3019,6 +3019,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
" \"CONSERVATIVE\"\n"
" }\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"
"{\n"
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
Expand Down Expand Up @@ -3055,7 +3058,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
coinControl.fAllowWatchOnly = request.params[1].get_bool();
}
else {
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ, UniValue::VBOOL});

UniValue options = request.params[1];

Expand Down Expand Up @@ -3124,8 +3127,11 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)

// parse hex string from parameter
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");
}

if (tx.vout.size() == 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
Expand Down Expand Up @@ -3443,7 +3449,7 @@ extern UniValue rescanblockchain(const JSONRPCRequest& request);
static const CRPCCommand commands[] =
{ // category name actor (function) argNames
// --------------------- ------------------------ ----------------------- ----------
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options"} },
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
{ "hidden", "resendwallettransactions", &resendwallettransactions, {} },
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
{ "wallet", "abortrescan", &abortrescan, {} },
Expand Down
11 changes: 11 additions & 0 deletions test/functional/rawtransactions.py
Expand Up @@ -253,6 +253,17 @@ def run_test(self):
self.sync_all()
assert_equal(self.nodes[0].getbalance(), bal+Decimal('50.00000000')+Decimal('2.19000000')) #block reward + tx

# decoderawtransaction tests
# witness transaction
encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000000000000"
decrawtx = self.nodes[0].decoderawtransaction(encrawtx, True) # decode as witness transaction
assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000'))
assert_raises_jsonrpc(-22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx, False) # force decode as non-witness transaction
# non-witness transaction
encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000"
decrawtx = self.nodes[0].decoderawtransaction(encrawtx, False) # decode as non-witness transaction
assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000'))

# getrawtransaction tests
# 1. valid parameters - only supply txid
txHash = rawTx["hash"]
Expand Down

0 comments on commit fee0370

Please sign in to comment.