RPC: getblock: tx fee calculation for verbosity 2 via Undo data

Co-authored-by: Felix Weis <mail@felixweis.com>
This commit is contained in:
Elliott Jin
2020-10-09 08:50:44 -07:00
parent cce1513179
commit bf7d6e31b1
3 changed files with 41 additions and 11 deletions

View File

@ -18,6 +18,7 @@ class CTransaction;
struct CMutableTransaction; struct CMutableTransaction;
class uint256; class uint256;
class UniValue; class UniValue;
class CTxUndo;
// core_read.cpp // core_read.cpp
CScript ParseScript(const std::string& s); CScript ParseScript(const std::string& s);
@ -45,6 +46,6 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
std::string SighashToStr(unsigned char sighash_type); std::string SighashToStr(unsigned char sighash_type);
void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
void ScriptToUniv(const CScript& script, UniValue& out, bool include_address); void ScriptToUniv(const CScript& script, UniValue& out, bool include_address);
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0); void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
#endif // BITCOIN_CORE_IO_H #endif // BITCOIN_CORE_IO_H

View File

@ -11,7 +11,9 @@
#include <script/standard.h> #include <script/standard.h>
#include <serialize.h> #include <serialize.h>
#include <streams.h> #include <streams.h>
#include <undo.h>
#include <univalue.h> #include <univalue.h>
#include <util/check.h>
#include <util/system.h> #include <util/system.h>
#include <util/strencodings.h> #include <util/strencodings.h>
@ -177,7 +179,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
out.pushKV("addresses", a); out.pushKV("addresses", a);
} }
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags) void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo)
{ {
entry.pushKV("txid", tx.GetHash().GetHex()); entry.pushKV("txid", tx.GetHash().GetHex());
entry.pushKV("hash", tx.GetWitnessHash().GetHex()); entry.pushKV("hash", tx.GetWitnessHash().GetHex());
@ -189,13 +191,20 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
entry.pushKV("weight", GetTransactionWeight(tx)); entry.pushKV("weight", GetTransactionWeight(tx));
entry.pushKV("locktime", (int64_t)tx.nLockTime); entry.pushKV("locktime", (int64_t)tx.nLockTime);
UniValue vin(UniValue::VARR); UniValue vin{UniValue::VARR};
// If available, use Undo data to calculate the fee. Note that txundo == nullptr
// for coinbase transactions and for transactions where undo data is unavailable.
const bool calculate_fee = txundo != nullptr;
CAmount amt_total_in = 0;
CAmount amt_total_out = 0;
for (unsigned int i = 0; i < tx.vin.size(); i++) { for (unsigned int i = 0; i < tx.vin.size(); i++) {
const CTxIn& txin = tx.vin[i]; const CTxIn& txin = tx.vin[i];
UniValue in(UniValue::VOBJ); UniValue in(UniValue::VOBJ);
if (tx.IsCoinBase()) if (tx.IsCoinBase()) {
in.pushKV("coinbase", HexStr(txin.scriptSig)); in.pushKV("coinbase", HexStr(txin.scriptSig));
else { } else {
in.pushKV("txid", txin.prevout.hash.GetHex()); in.pushKV("txid", txin.prevout.hash.GetHex());
in.pushKV("vout", (int64_t)txin.prevout.n); in.pushKV("vout", (int64_t)txin.prevout.n);
UniValue o(UniValue::VOBJ); UniValue o(UniValue::VOBJ);
@ -210,6 +219,10 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
} }
in.pushKV("txinwitness", txinwitness); in.pushKV("txinwitness", txinwitness);
} }
if (calculate_fee) {
const CTxOut& prev_txout = txundo->vprevout[i].out;
amt_total_in += prev_txout.nValue;
}
in.pushKV("sequence", (int64_t)txin.nSequence); in.pushKV("sequence", (int64_t)txin.nSequence);
vin.push_back(in); vin.push_back(in);
} }
@ -228,9 +241,19 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
ScriptPubKeyToUniv(txout.scriptPubKey, o, true); ScriptPubKeyToUniv(txout.scriptPubKey, o, true);
out.pushKV("scriptPubKey", o); out.pushKV("scriptPubKey", o);
vout.push_back(out); vout.push_back(out);
if (calculate_fee) {
amt_total_out += txout.nValue;
}
} }
entry.pushKV("vout", vout); entry.pushKV("vout", vout);
if (calculate_fee) {
const CAmount fee = amt_total_in - amt_total_out;
CHECK_NONFATAL(MoneyRange(fee));
entry.pushKV("fee", ValueFromAmount(fee));
}
if (!hashBlock.IsNull()) if (!hashBlock.IsNull())
entry.pushKV("blockhash", hashBlock.GetHex()); entry.pushKV("blockhash", hashBlock.GetHex());

View File

@ -162,17 +162,22 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
result.pushKV("versionHex", strprintf("%08x", block.nVersion)); result.pushKV("versionHex", strprintf("%08x", block.nVersion));
result.pushKV("merkleroot", block.hashMerkleRoot.GetHex()); result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
UniValue txs(UniValue::VARR); UniValue txs(UniValue::VARR);
for(const auto& tx : block.vtx) if (txDetails) {
{ CBlockUndo blockUndo;
if(txDetails) const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
{ for (size_t i = 0; i < block.vtx.size(); ++i) {
const CTransactionRef& tx = block.vtx.at(i);
// coinbase transaction (i == 0) doesn't have undo data
const CTxUndo* txundo = (have_undo && i) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
UniValue objTx(UniValue::VOBJ); UniValue objTx(UniValue::VOBJ);
TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags()); TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags(), txundo);
txs.push_back(objTx); txs.push_back(objTx);
} }
else } else {
for (const CTransactionRef& tx : block.vtx) {
txs.push_back(tx->GetHash().GetHex()); txs.push_back(tx->GetHash().GetHex());
} }
}
result.pushKV("tx", txs); result.pushKV("tx", txs);
result.pushKV("time", block.GetBlockTime()); result.pushKV("time", block.GetBlockTime());
result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast()); result.pushKV("mediantime", (int64_t)blockindex->GetMedianTimePast());
@ -926,6 +931,7 @@ static RPCHelpMan getblock()
{RPCResult::Type::OBJ, "", "", {RPCResult::Type::OBJ, "", "",
{ {
{RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"}, {RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"},
{RPCResult::Type::NUM, "fee", "The transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"},
}}, }},
}}, }},
{RPCResult::Type::ELISION, "", "Same output as verbosity = 1"}, {RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},