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 https://github.com/bitcoin/bitcoin/pull/10481#issuecomment-325244946, 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
This commit is contained in:
commit
fee0370fd6
7 changed files with 56 additions and 29 deletions
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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" },
|
||||
|
|
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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"} },
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
@ -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"
|
||||
|
@ -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];
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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, {} },
|
||||
|
|
|
@ -253,6 +253,17 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||
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"]
|
||||
|
|
Loading…
Reference in a new issue