aboutsummaryrefslogtreecommitdiff
path: root/src/rpc
diff options
context:
space:
mode:
authorAndrew Chow <github@achow101.com>2022-12-13 17:59:26 -0500
committerAndrew Chow <github@achow101.com>2022-12-13 18:09:09 -0500
commitdaf881de9db195f0ce5b8067edba2eef7a962abf (patch)
treeb613786a30fbef6b1471ee81b51a2c6483c6587f /src/rpc
parentffa32ab108a4bfb53f012549bc4e3c224a6aca3e (diff)
parentf86697163e5cdbc3bc4a65cfb7dbaa3d9eb602a9 (diff)
downloadbitcoin-daf881de9db195f0ce5b8067edba2eef7a962abf.tar.xz
Merge bitcoin/bitcoin#23319: rpc: Return fee and prevout (utxos) to getrawtransaction
f86697163e5cdbc3bc4a65cfb7dbaa3d9eb602a9 rpc: Return fee and prevout(s) to getrawtransaction (Douglas Chimento) Pull request description: Add fee response in BTC to getrawtransaction #23264 ### For Reviewers * Verbose arg is now an int * Verbose = 2 includes a `fee` field and `prevout` * [./test/functional/rpc_rawtransaction.py](./test/functional/rpc_rawtransaction.py) contains a new test to validate fields of new verbosity 2 (not the values) ``` bitcoin-cli -chain=test getrawtransaction 9ae533f7da9be4a34997db78343a8d8d6d6186b6bba3959e56f416a5c70e7de4 2 000000000000001d442e556146d5f2841d85150c200e8d8b8a4b5005b13878f6 ``` ``` "in_active_chain": true, "txid": "9ae533f7da9be4a34997db78343a8d8d6d6186b6bba3959e56f416a5c70e7de4", "hash": "7f23e3f3a0a256ddea1d35ffd43e9afdd67cc68389ef1a804bb20c76abd6863e", .... "vin": [ { "txid": "23fc75d6d74f6f97e225839af69ff36a612fe04db58a4414ec4828d1749a05a0", "vout": 0, "scriptSig": { "asm": "", "hex": "" }, "prevout": { "generated": false, "height": 2099486, "value": 0.00017764, "scriptPubKey": { "asm": "0 7846ce1ced3253d8bd43008db2ca364cc722f5a2", "hex": "00147846ce1ced3253d8bd43008db2ca364cc722f5a2", "address": "tb1q0prvu88dxffa302rqzxm9j3kfnrj9adzk49mlp", "type": "witness_v0_keyhash" } }, "sequence": 4294967295 }, ... "fee": 0.00000762 } ``` ACKs for top commit: achow101: ACK f86697163e5cdbc3bc4a65cfb7dbaa3d9eb602a9 aureleoules: ACK f86697163e5cdbc3bc4a65cfb7dbaa3d9eb602a9 hernanmarino: re ACK f86697163e5cdbc3bc4a65cfb7dbaa3d9eb602a9 pablomartin4btc: re-tACK f86697163e5cdbc3bc4a65cfb7dbaa3d9eb602a9 Tree-SHA512: 591fdc285d74fa7803e04ad01c7b70bc20fac6b1369e7bd5b8e2cde9b750ea52d6c70d79225b74bef4f4bbc0fb960877778017184e146119da4a55f9593d1224
Diffstat (limited to 'src/rpc')
-rw-r--r--src/rpc/client.cpp1
-rw-r--r--src/rpc/rawtransaction.cpp112
2 files changed, 89 insertions, 24 deletions
diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp
index ea094976bf..a795296f35 100644
--- a/src/rpc/client.cpp
+++ b/src/rpc/client.cpp
@@ -102,6 +102,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
{ "getchaintxstats", 0, "nblocks" },
{ "gettransaction", 1, "include_watchonly" },
{ "gettransaction", 2, "verbose" },
+ { "getrawtransaction", 1, "verbosity" },
{ "getrawtransaction", 1, "verbose" },
{ "createrawtransaction", 0, "inputs" },
{ "createrawtransaction", 1, "outputs" },
diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp
index 400f2f1507..df30eaaa97 100644
--- a/src/rpc/rawtransaction.cpp
+++ b/src/rpc/rawtransaction.cpp
@@ -32,6 +32,7 @@
#include <script/signingprovider.h>
#include <script/standard.h>
#include <uint256.h>
+#include <undo.h>
#include <util/bip32.h>
#include <util/check.h>
#include <util/strencodings.h>
@@ -50,15 +51,17 @@ using node::FindCoins;
using node::GetTransaction;
using node::NodeContext;
using node::PSBTAnalysis;
+using node::ReadBlockFromDisk;
+using node::UndoReadFromDisk;
-static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, Chainstate& active_chainstate)
+static void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, Chainstate& active_chainstate, const CTxUndo* txundo = nullptr, TxVerbosity verbosity = TxVerbosity::SHOW_TXID)
{
// Call into TxToUniv() in bitcoin-common to decode the transaction hex.
//
// Blockchain contextual information (confirmations and blocktime) is not
// available to code in bitcoin-common, so we query them here and push the
// data into the returned UniValue.
- TxToUniv(tx, /*block_hash=*/uint256(), entry, /*include_hex=*/true, RPCSerializationFlags());
+ TxToUniv(tx, /*block_hash=*/uint256(), entry, /*include_hex=*/true, RPCSerializationFlags(), txundo, verbosity);
if (!hashBlock.IsNull()) {
LOCK(cs_main);
@@ -166,26 +169,27 @@ static RPCHelpMan getrawtransaction()
{
return RPCHelpMan{
"getrawtransaction",
- "Return the raw transaction data.\n"
- "\nBy default, this call only returns a transaction if it is in the mempool. If -txindex is enabled\n"
+ "By default, this call only returns a transaction if it is in the mempool. If -txindex is enabled\n"
"and no blockhash argument is passed, it will return the transaction if it is in the mempool or any block.\n"
"If a blockhash argument is passed, it will return the transaction if\n"
- "the specified block is available and the transaction is in that block.\n"
- "\nHint: Use gettransaction for wallet transactions.\n"
+ "the specified block is available and the transaction is in that block.\n\n"
+ "Hint: Use gettransaction for wallet transactions.\n\n"
- "\nIf verbose is 'true', returns an Object with information about 'txid'.\n"
- "If verbose is 'false' or omitted, returns a string that is serialized, hex-encoded data for 'txid'.",
+ "If verbosity is 0 or omitted, returns the serialized transaction as a hex-encoded string.\n"
+ "If verbosity is 1, returns a JSON Object with information about transaction.\n"
+ "If verbosity is 2, returns a JSON Object with information about transaction, including fee and prevout information.",
{
{"txid", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction id"},
- {"verbose", RPCArg::Type::BOOL, RPCArg::Default{false}, "If false, return a string, otherwise return a json object"},
+ {"verbosity|verbose", RPCArg::Type::NUM, RPCArg::Default{0}, "0 for hex-encoded data, 1 for a JSON object, and 2 for JSON object with fee and prevout"},
{"blockhash", RPCArg::Type::STR_HEX, RPCArg::Optional::OMITTED_NAMED_ARG, "The block in which to look for the transaction"},
},
{
- RPCResult{"if verbose is not set or set to false",
- RPCResult::Type::STR, "data", "The serialized, hex-encoded data for 'txid'"
+ RPCResult{"if verbosity is not set or set to 0",
+ RPCResult::Type::STR, "data", "The serialized transaction as a hex-encoded string for 'txid'"
},
- RPCResult{"if verbose is set to true",
+ RPCResult{"if verbosity is set to 1",
+ // When updating this documentation, update `decoderawtransaction` in the same way.
RPCResult::Type::OBJ, "", "",
Cat<std::vector<RPCResult>>(
{
@@ -198,20 +202,47 @@ static RPCHelpMan getrawtransaction()
},
DecodeTxDoc(/*txid_field_doc=*/"The transaction id (same as provided)")),
},
+ RPCResult{"for verbosity = 2",
+ RPCResult::Type::OBJ, "", "",
+ {
+ {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},
+ {RPCResult::Type::NUM, "fee", /* optional */ true, "transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"},
+ {RPCResult::Type::ARR, "vin", "",
+ {
+ {RPCResult::Type::OBJ, "", /* optional */ true, "utxo being spent, omitted if block undo data is not available",
+ {
+ {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},
+ {RPCResult::Type::OBJ, "prevout", "Only if undo information is available)",
+ {
+ {RPCResult::Type::BOOL, "generated", "Coinbase or not"},
+ {RPCResult::Type::NUM, "height", "The height of the prevout"},
+ {RPCResult::Type::STR_AMOUNT, "value", "The value in " + CURRENCY_UNIT},
+ {RPCResult::Type::OBJ, "scriptPubKey", "",
+ {
+ {RPCResult::Type::STR, "asm", "Disassembly of the public key script"},
+ {RPCResult::Type::STR, "desc", "Inferred descriptor for the output"},
+ {RPCResult::Type::STR_HEX, "hex", "The raw public key script bytes, hex-encoded"},
+ {RPCResult::Type::STR, "address", /*optional=*/true, "The Bitcoin address (only if a well-defined address exists)"},
+ {RPCResult::Type::STR, "type", "The type (one of: " + GetAllOutputTypes() + ")"},
+ }},
+ }},
+ }},
+ }},
+ }},
},
RPCExamples{
HelpExampleCli("getrawtransaction", "\"mytxid\"")
- + HelpExampleCli("getrawtransaction", "\"mytxid\" true")
- + HelpExampleRpc("getrawtransaction", "\"mytxid\", true")
- + HelpExampleCli("getrawtransaction", "\"mytxid\" false \"myblockhash\"")
- + HelpExampleCli("getrawtransaction", "\"mytxid\" true \"myblockhash\"")
+ + HelpExampleCli("getrawtransaction", "\"mytxid\" 1")
+ + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1")
+ + HelpExampleCli("getrawtransaction", "\"mytxid\" 0 \"myblockhash\"")
+ + HelpExampleCli("getrawtransaction", "\"mytxid\" 1 \"myblockhash\"")
+ + HelpExampleCli("getrawtransaction", "\"mytxid\" 2 \"myblockhash\"")
},
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
{
const NodeContext& node = EnsureAnyNodeContext(request.context);
ChainstateManager& chainman = EnsureChainman(node);
- bool in_active_chain = true;
uint256 hash = ParseHashV(request.params[0], "parameter 1");
const CBlockIndex* blockindex = nullptr;
@@ -220,10 +251,14 @@ static RPCHelpMan getrawtransaction()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "The genesis block coinbase is not considered an ordinary transaction and cannot be retrieved");
}
- // Accept either a bool (true) or a num (>=1) to indicate verbose output.
- bool fVerbose = false;
+ // Accept either a bool (true) or a num (>=0) to indicate verbosity.
+ int verbosity{0};
if (!request.params[1].isNull()) {
- fVerbose = request.params[1].isNum() ? (request.params[1].getInt<int>() != 0) : request.params[1].get_bool();
+ if (request.params[1].isBool()) {
+ verbosity = request.params[1].get_bool();
+ } else {
+ verbosity = request.params[1].getInt<int>();
+ }
}
if (!request.params[2].isNull()) {
@@ -234,7 +269,6 @@ static RPCHelpMan getrawtransaction()
if (!blockindex) {
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found");
}
- in_active_chain = chainman.ActiveChain().Contains(blockindex);
}
bool f_txindex_ready = false;
@@ -262,13 +296,43 @@ static RPCHelpMan getrawtransaction()
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, errmsg + ". Use gettransaction for wallet transactions.");
}
- if (!fVerbose) {
+ if (verbosity <= 0) {
return EncodeHexTx(*tx, RPCSerializationFlags());
}
UniValue result(UniValue::VOBJ);
- if (blockindex) result.pushKV("in_active_chain", in_active_chain);
- TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate());
+ if (blockindex) {
+ LOCK(cs_main);
+ result.pushKV("in_active_chain", chainman.ActiveChain().Contains(blockindex));
+ }
+ // If request is verbosity >= 1 but no blockhash was given, then look up the blockindex
+ if (request.params[2].isNull()) {
+ LOCK(cs_main);
+ blockindex = chainman.m_blockman.LookupBlockIndex(hash_block);
+ }
+ if (verbosity == 1) {
+ TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate());
+ return result;
+ }
+
+ CBlockUndo blockUndo;
+ CBlock block;
+ const bool is_block_pruned{WITH_LOCK(cs_main, return chainman.m_blockman.IsBlockPruned(blockindex))};
+
+ if (tx->IsCoinBase() ||
+ !blockindex || is_block_pruned ||
+ !(UndoReadFromDisk(blockUndo, blockindex) && ReadBlockFromDisk(block, blockindex, Params().GetConsensus()))) {
+ TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate());
+ return result;
+ }
+
+ CTxUndo* undoTX {nullptr};
+ auto it = std::find_if(block.vtx.begin(), block.vtx.end(), [tx](CTransactionRef t){ return *t == *tx; });
+ if (it != block.vtx.end()) {
+ // -1 as blockundo does not have coinbase tx
+ undoTX = &blockUndo.vtxundo.at(it - block.vtx.begin() - 1);
+ }
+ TxToJSON(*tx, hash_block, result, chainman.ActiveChainstate(), undoTX, TxVerbosity::SHOW_DETAILS_AND_PREVOUT);
return result;
},
};