Skip to content

Commit fee0370

Browse files
committed
Merge #11178: Add iswitness parameter to decode- and fundrawtransaction RPCs
6f39ac0 Add test for decoderawtransaction bool (MeshCollider) bbdbe80 Add iswitness parameter to decode- and fundrawtransaction RPCs (MeshCollider) Pull request description: Suggested in bitcoin/bitcoin#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
2 parents 483bb67 + 6f39ac0 commit fee0370

File tree

7 files changed

+56
-29
lines changed

7 files changed

+56
-29
lines changed

src/core_io.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class UniValue;
2020
// core_read.cpp
2121
CScript ParseScript(const std::string& s);
2222
std::string ScriptToAsmStr(const CScript& script, const bool fAttemptSighashDecode = false);
23-
bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness = false);
23+
bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness = false, bool try_witness = true);
2424
bool DecodeHexBlk(CBlock&, const std::string& strHexBlk);
2525
uint256 ParseHashUV(const UniValue& v, const std::string& strName);
2626
uint256 ParseHashStr(const std::string&, const std::string& strName);

src/core_read.cpp

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -108,39 +108,39 @@ bool CheckTxScriptsSanity(const CMutableTransaction& tx)
108108
return true;
109109
}
110110

111-
bool DecodeHexTx(CMutableTransaction& tx, const std::string& strHexTx, bool fTryNoWitness)
111+
bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness, bool try_witness)
112112
{
113-
if (!IsHex(strHexTx)) {
113+
if (!IsHex(hex_tx)) {
114114
return false;
115115
}
116116

117-
std::vector<unsigned char> txData(ParseHex(strHexTx));
117+
std::vector<unsigned char> txData(ParseHex(hex_tx));
118118

119-
if (fTryNoWitness) {
119+
if (try_no_witness) {
120120
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS);
121121
try {
122122
ssData >> tx;
123-
if (ssData.eof() && CheckTxScriptsSanity(tx)) {
123+
if (ssData.eof() && (!try_witness || CheckTxScriptsSanity(tx))) {
124124
return true;
125125
}
126-
}
127-
catch (const std::exception&) {
126+
} catch (const std::exception&) {
128127
// Fall through.
129128
}
130129
}
131130

132-
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
133-
try {
134-
ssData >> tx;
135-
if (!ssData.empty()) {
136-
return false;
131+
if (try_witness) {
132+
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
133+
try {
134+
ssData >> tx;
135+
if (ssData.empty()) {
136+
return true;
137+
}
138+
} catch (const std::exception&) {
139+
// Fall through.
137140
}
138141
}
139-
catch (const std::exception&) {
140-
return false;
141-
}
142-
143-
return true;
142+
143+
return false;
144144
}
145145

146146
bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk)

src/rpc/client.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,13 @@ static const CRPCConvertParam vRPCConvertParams[] =
9191
{ "createrawtransaction", 1, "outputs" },
9292
{ "createrawtransaction", 2, "locktime" },
9393
{ "createrawtransaction", 3, "replaceable" },
94+
{ "decoderawtransaction", 1, "iswitness" },
9495
{ "signrawtransaction", 1, "prevtxs" },
9596
{ "signrawtransaction", 2, "privkeys" },
9697
{ "sendrawtransaction", 1, "allowhighfees" },
9798
{ "combinerawtransaction", 0, "txs" },
9899
{ "fundrawtransaction", 1, "options" },
100+
{ "fundrawtransaction", 2, "iswitness" },
99101
{ "gettxout", 1, "n" },
100102
{ "gettxout", 2, "include_mempool" },
101103
{ "gettxoutproof", 0, "txids" },

src/rpc/rawtransaction.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -441,13 +441,15 @@ UniValue createrawtransaction(const JSONRPCRequest& request)
441441

442442
UniValue decoderawtransaction(const JSONRPCRequest& request)
443443
{
444-
if (request.fHelp || request.params.size() != 1)
444+
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
445445
throw std::runtime_error(
446-
"decoderawtransaction \"hexstring\"\n"
446+
"decoderawtransaction \"hexstring\" ( iswitness )\n"
447447
"\nReturn a JSON object representing the serialized, hex-encoded transaction.\n"
448448

449449
"\nArguments:\n"
450450
"1. \"hexstring\" (string, required) The transaction hex string\n"
451+
"2. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction\n"
452+
" If iswitness is not present, heuristic tests will be used in decoding\n"
451453

452454
"\nResult:\n"
453455
"{\n"
@@ -495,12 +497,16 @@ UniValue decoderawtransaction(const JSONRPCRequest& request)
495497
);
496498

497499
LOCK(cs_main);
498-
RPCTypeCheck(request.params, {UniValue::VSTR});
500+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
499501

500502
CMutableTransaction mtx;
501503

502-
if (!DecodeHexTx(mtx, request.params[0].get_str(), true))
504+
bool try_witness = request.params[1].isNull() ? true : request.params[1].get_bool();
505+
bool try_no_witness = request.params[1].isNull() ? true : !request.params[1].get_bool();
506+
507+
if (!DecodeHexTx(mtx, request.params[0].get_str(), try_no_witness, try_witness)) {
503508
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
509+
}
504510

505511
UniValue result(UniValue::VOBJ);
506512
TxToUniv(CTransaction(std::move(mtx)), uint256(), result, false);
@@ -1016,7 +1022,7 @@ static const CRPCCommand commands[] =
10161022
// --------------------- ------------------------ ----------------------- ----------
10171023
{ "rawtransactions", "getrawtransaction", &getrawtransaction, {"txid","verbose","blockhash"} },
10181024
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
1019-
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring"} },
1025+
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
10201026
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
10211027
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
10221028
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },

src/test/rpc_tests.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,9 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams)
6565
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "size").get_int(), 193);
6666
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1);
6767
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0);
68-
BOOST_CHECK_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" extra"), std::runtime_error);
68+
BOOST_CHECK_THROW(CallRPC(std::string("decoderawtransaction ")+rawtx+" extra"), std::runtime_error);
69+
BOOST_CHECK_NO_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false"));
70+
BOOST_CHECK_THROW(r = CallRPC(std::string("decoderawtransaction ")+rawtx+" false extra"), std::runtime_error);
6971

7072
BOOST_CHECK_THROW(CallRPC("signrawtransaction"), std::runtime_error);
7173
BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), std::runtime_error);

src/wallet/rpcwallet.cpp

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2982,9 +2982,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
29822982
return NullUniValue;
29832983
}
29842984

2985-
if (request.fHelp || request.params.size() < 1 || request.params.size() > 2)
2985+
if (request.fHelp || request.params.size() < 1 || request.params.size() > 3)
29862986
throw std::runtime_error(
2987-
"fundrawtransaction \"hexstring\" ( options )\n"
2987+
"fundrawtransaction \"hexstring\" ( options iswitness )\n"
29882988
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
29892989
"This will not modify existing inputs, and will add at most one change output to the outputs.\n"
29902990
"No existing outputs will be modified unless \"subtractFeeFromOutputs\" is specified.\n"
@@ -3019,6 +3019,9 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
30193019
" \"CONSERVATIVE\"\n"
30203020
" }\n"
30213021
" for backward compatibility: passing in a true instead of an object will result in {\"includeWatching\":true}\n"
3022+
"3. iswitness (boolean, optional) Whether the transaction hex is a serialized witness transaction \n"
3023+
" If iswitness is not present, heuristic tests will be used in decoding\n"
3024+
30223025
"\nResult:\n"
30233026
"{\n"
30243027
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
@@ -3055,7 +3058,7 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
30553058
coinControl.fAllowWatchOnly = request.params[1].get_bool();
30563059
}
30573060
else {
3058-
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ});
3061+
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VOBJ, UniValue::VBOOL});
30593062

30603063
UniValue options = request.params[1];
30613064

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

31253128
// parse hex string from parameter
31263129
CMutableTransaction tx;
3127-
if (!DecodeHexTx(tx, request.params[0].get_str(), true))
3130+
bool try_witness = request.params[2].isNull() ? true : request.params[2].get_bool();
3131+
bool try_no_witness = request.params[2].isNull() ? true : !request.params[2].get_bool();
3132+
if (!DecodeHexTx(tx, request.params[0].get_str(), try_no_witness, try_witness)) {
31283133
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
3134+
}
31293135

31303136
if (tx.vout.size() == 0)
31313137
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
@@ -3443,7 +3449,7 @@ extern UniValue rescanblockchain(const JSONRPCRequest& request);
34433449
static const CRPCCommand commands[] =
34443450
{ // category name actor (function) argNames
34453451
// --------------------- ------------------------ ----------------------- ----------
3446-
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options"} },
3452+
{ "rawtransactions", "fundrawtransaction", &fundrawtransaction, {"hexstring","options","iswitness"} },
34473453
{ "hidden", "resendwallettransactions", &resendwallettransactions, {} },
34483454
{ "wallet", "abandontransaction", &abandontransaction, {"txid"} },
34493455
{ "wallet", "abortrescan", &abortrescan, {} },

test/functional/rawtransactions.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,17 @@ def run_test(self):
253253
self.sync_all()
254254
assert_equal(self.nodes[0].getbalance(), bal+Decimal('50.00000000')+Decimal('2.19000000')) #block reward + tx
255255

256+
# decoderawtransaction tests
257+
# witness transaction
258+
encrawtx = "010000000001010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f50500000000000000000000"
259+
decrawtx = self.nodes[0].decoderawtransaction(encrawtx, True) # decode as witness transaction
260+
assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000'))
261+
assert_raises_jsonrpc(-22, 'TX decode failed', self.nodes[0].decoderawtransaction, encrawtx, False) # force decode as non-witness transaction
262+
# non-witness transaction
263+
encrawtx = "01000000010000000000000072c1a6a246ae63f74f931e8365e15a089c68d61900000000000000000000ffffffff0100e1f505000000000000000000"
264+
decrawtx = self.nodes[0].decoderawtransaction(encrawtx, False) # decode as non-witness transaction
265+
assert_equal(decrawtx['vout'][0]['value'], Decimal('1.00000000'))
266+
256267
# getrawtransaction tests
257268
# 1. valid parameters - only supply txid
258269
txHash = rawTx["hash"]

0 commit comments

Comments
 (0)