Skip to content

Commit bf7d6e3

Browse files
RPC: getblock: tx fee calculation for verbosity 2 via Undo data
Co-authored-by: Felix Weis <[email protected]>
1 parent cce1513 commit bf7d6e3

File tree

3 files changed

+41
-11
lines changed

3 files changed

+41
-11
lines changed

src/core_io.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class CTransaction;
1818
struct CMutableTransaction;
1919
class uint256;
2020
class UniValue;
21+
class CTxUndo;
2122

2223
// core_read.cpp
2324
CScript ParseScript(const std::string& s);
@@ -45,6 +46,6 @@ std::string EncodeHexTx(const CTransaction& tx, const int serializeFlags = 0);
4546
std::string SighashToStr(unsigned char sighash_type);
4647
void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex);
4748
void ScriptToUniv(const CScript& script, UniValue& out, bool include_address);
48-
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0);
49+
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex = true, int serialize_flags = 0, const CTxUndo* txundo = nullptr);
4950

5051
#endif // BITCOIN_CORE_IO_H

src/core_write.cpp

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
#include <script/standard.h>
1212
#include <serialize.h>
1313
#include <streams.h>
14+
#include <undo.h>
1415
#include <univalue.h>
16+
#include <util/check.h>
1517
#include <util/system.h>
1618
#include <util/strencodings.h>
1719

@@ -177,7 +179,7 @@ void ScriptPubKeyToUniv(const CScript& scriptPubKey,
177179
out.pushKV("addresses", a);
178180
}
179181

180-
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags)
182+
void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags, const CTxUndo* txundo)
181183
{
182184
entry.pushKV("txid", tx.GetHash().GetHex());
183185
entry.pushKV("hash", tx.GetWitnessHash().GetHex());
@@ -189,13 +191,20 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
189191
entry.pushKV("weight", GetTransactionWeight(tx));
190192
entry.pushKV("locktime", (int64_t)tx.nLockTime);
191193

192-
UniValue vin(UniValue::VARR);
194+
UniValue vin{UniValue::VARR};
195+
196+
// If available, use Undo data to calculate the fee. Note that txundo == nullptr
197+
// for coinbase transactions and for transactions where undo data is unavailable.
198+
const bool calculate_fee = txundo != nullptr;
199+
CAmount amt_total_in = 0;
200+
CAmount amt_total_out = 0;
201+
193202
for (unsigned int i = 0; i < tx.vin.size(); i++) {
194203
const CTxIn& txin = tx.vin[i];
195204
UniValue in(UniValue::VOBJ);
196-
if (tx.IsCoinBase())
205+
if (tx.IsCoinBase()) {
197206
in.pushKV("coinbase", HexStr(txin.scriptSig));
198-
else {
207+
} else {
199208
in.pushKV("txid", txin.prevout.hash.GetHex());
200209
in.pushKV("vout", (int64_t)txin.prevout.n);
201210
UniValue o(UniValue::VOBJ);
@@ -210,6 +219,10 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
210219
}
211220
in.pushKV("txinwitness", txinwitness);
212221
}
222+
if (calculate_fee) {
223+
const CTxOut& prev_txout = txundo->vprevout[i].out;
224+
amt_total_in += prev_txout.nValue;
225+
}
213226
in.pushKV("sequence", (int64_t)txin.nSequence);
214227
vin.push_back(in);
215228
}
@@ -228,9 +241,19 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry,
228241
ScriptPubKeyToUniv(txout.scriptPubKey, o, true);
229242
out.pushKV("scriptPubKey", o);
230243
vout.push_back(out);
244+
245+
if (calculate_fee) {
246+
amt_total_out += txout.nValue;
247+
}
231248
}
232249
entry.pushKV("vout", vout);
233250

251+
if (calculate_fee) {
252+
const CAmount fee = amt_total_in - amt_total_out;
253+
CHECK_NONFATAL(MoneyRange(fee));
254+
entry.pushKV("fee", ValueFromAmount(fee));
255+
}
256+
234257
if (!hashBlock.IsNull())
235258
entry.pushKV("blockhash", hashBlock.GetHex());
236259

src/rpc/blockchain.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -162,16 +162,21 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* tip, const CBlockIn
162162
result.pushKV("versionHex", strprintf("%08x", block.nVersion));
163163
result.pushKV("merkleroot", block.hashMerkleRoot.GetHex());
164164
UniValue txs(UniValue::VARR);
165-
for(const auto& tx : block.vtx)
166-
{
167-
if(txDetails)
168-
{
165+
if (txDetails) {
166+
CBlockUndo blockUndo;
167+
const bool have_undo = !IsBlockPruned(blockindex) && UndoReadFromDisk(blockUndo, blockindex);
168+
for (size_t i = 0; i < block.vtx.size(); ++i) {
169+
const CTransactionRef& tx = block.vtx.at(i);
170+
// coinbase transaction (i == 0) doesn't have undo data
171+
const CTxUndo* txundo = (have_undo && i) ? &blockUndo.vtxundo.at(i - 1) : nullptr;
169172
UniValue objTx(UniValue::VOBJ);
170-
TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags());
173+
TxToUniv(*tx, uint256(), objTx, true, RPCSerializationFlags(), txundo);
171174
txs.push_back(objTx);
172175
}
173-
else
176+
} else {
177+
for (const CTransactionRef& tx : block.vtx) {
174178
txs.push_back(tx->GetHash().GetHex());
179+
}
175180
}
176181
result.pushKV("tx", txs);
177182
result.pushKV("time", block.GetBlockTime());
@@ -926,6 +931,7 @@ static RPCHelpMan getblock()
926931
{RPCResult::Type::OBJ, "", "",
927932
{
928933
{RPCResult::Type::ELISION, "", "The transactions in the format of the getrawtransaction RPC. Different from verbosity = 1 \"tx\" result"},
934+
{RPCResult::Type::NUM, "fee", "The transaction fee in " + CURRENCY_UNIT + ", omitted if block undo data is not available"},
929935
}},
930936
}},
931937
{RPCResult::Type::ELISION, "", "Same output as verbosity = 1"},

0 commit comments

Comments
 (0)