Skip to content

Commit 78af107

Browse files
committed
rpc: add coinbase_tx field to getblock
This adds a "coinbase_tx" field to the getblock RPC result, starting at verbosity level 1. It contains only fields guaranteed to be small, i.e. not the outputs.
1 parent 41b9b76 commit 78af107

File tree

3 files changed

+62
-0
lines changed

3 files changed

+62
-0
lines changed

doc/release-notes-34512.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
Updated RPCs
2+
------------
3+
4+
- The `getblock` RPC now returns a `coinbase_tx` object at verbosity levels 1, 2,
5+
and 3. It contains `version`, `locktime`, `sequence`, `coinbase` and
6+
`witness`. This allows for efficiently querying coinbase
7+
transaction properties without fetching the full transaction data at
8+
verbosity 2+. (#xxxxx)

src/rpc/blockchain.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,13 +181,35 @@ UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex
181181
return result;
182182
}
183183

184+
/** Serialize coinbase transaction metadata */
185+
UniValue coinbaseTxToJSON(const CTransaction& coinbase_tx)
186+
{
187+
CHECK_NONFATAL(!coinbase_tx.vin.empty());
188+
const CTxIn& vin_0{coinbase_tx.vin[0]};
189+
UniValue coinbase_tx_obj(UniValue::VOBJ);
190+
coinbase_tx_obj.pushKV("version", coinbase_tx.version);
191+
coinbase_tx_obj.pushKV("locktime", coinbase_tx.nLockTime);
192+
coinbase_tx_obj.pushKV("sequence", vin_0.nSequence);
193+
coinbase_tx_obj.pushKV("coinbase", HexStr(vin_0.scriptSig));
194+
const auto& witness_stack{vin_0.scriptWitness.stack};
195+
if (!witness_stack.empty()) {
196+
CHECK_NONFATAL(witness_stack.size() == 1);
197+
coinbase_tx_obj.pushKV("witness", HexStr(witness_stack[0]));
198+
}
199+
return coinbase_tx_obj;
200+
}
201+
184202
UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity, const uint256 pow_limit)
185203
{
186204
UniValue result = blockheaderToJSON(tip, blockindex, pow_limit);
187205

188206
result.pushKV("strippedsize", ::GetSerializeSize(TX_NO_WITNESS(block)));
189207
result.pushKV("size", ::GetSerializeSize(TX_WITH_WITNESS(block)));
190208
result.pushKV("weight", ::GetBlockWeight(block));
209+
210+
CHECK_NONFATAL(!block.vtx.empty());
211+
result.pushKV("coinbase_tx", coinbaseTxToJSON(*block.vtx[0]));
212+
191213
UniValue txs(UniValue::VARR);
192214
txs.reserve(block.vtx.size());
193215

@@ -760,6 +782,14 @@ static RPCHelpMan getblock()
760782
{RPCResult::Type::NUM, "size", "The block size"},
761783
{RPCResult::Type::NUM, "strippedsize", "The block size excluding witness data"},
762784
{RPCResult::Type::NUM, "weight", "The block weight as defined in BIP 141"},
785+
{RPCResult::Type::OBJ, "coinbase_tx", "Coinbase transaction metadata",
786+
{
787+
{RPCResult::Type::NUM, "version", "The coinbase transaction version"},
788+
{RPCResult::Type::NUM, "locktime", "The coinbase transaction's locktime (nLockTime)"},
789+
{RPCResult::Type::NUM, "sequence", "The coinbase input's sequence number (nSequence)"},
790+
{RPCResult::Type::STR_HEX, "coinbase", "The coinbase input's scriptSig"},
791+
{RPCResult::Type::STR_HEX, "witness", /*optional=*/true, "The coinbase input's first (and only) witness stack element, if present"},
792+
}},
763793
{RPCResult::Type::NUM, "height", "The block height or index"},
764794
{RPCResult::Type::NUM, "version", "The block version"},
765795
{RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},

test/functional/rpc_blockchain.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
from test_framework.messages import (
4343
CBlockHeader,
4444
COIN,
45+
SEQUENCE_FINAL,
4546
from_hex,
4647
msg_block,
4748
)
@@ -646,6 +647,26 @@ def _test_getblock(self):
646647
self.wallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
647648
blockhash = self.generate(node, 1)[0]
648649

650+
def assert_coinbase_metadata(hash, verbosity):
651+
block = node.getblock(hash, verbosity)
652+
coinbase_tx = node.getblock(hash, 2)["tx"][0]
653+
654+
expected_keys = {"version", "locktime", "sequence", "coinbase"}
655+
if "txinwitness" in coinbase_tx["vin"][0]:
656+
expected_keys.add("witness")
657+
assert_equal(set(block["coinbase_tx"].keys()), expected_keys)
658+
659+
assert_equal(block["coinbase_tx"]["version"], coinbase_tx["version"])
660+
assert_equal(block["coinbase_tx"]["locktime"], coinbase_tx["locktime"])
661+
assert_equal(block["coinbase_tx"]["sequence"], coinbase_tx["vin"][0]["sequence"])
662+
assert_equal(block["coinbase_tx"]["coinbase"], coinbase_tx["vin"][0]["coinbase"])
663+
664+
witness_stack = coinbase_tx["vin"][0].get("txinwitness")
665+
if witness_stack is None:
666+
assert "witness" not in block["coinbase_tx"]
667+
else:
668+
assert_equal(block["coinbase_tx"]["witness"], witness_stack[0])
669+
649670
def assert_hexblock_hashes(verbosity):
650671
block = node.getblock(blockhash, verbosity)
651672
assert_equal(blockhash, hash256(bytes.fromhex(block[:160]))[::-1].hex())
@@ -692,6 +713,9 @@ def assert_vin_does_not_contain_prevout(hash, verbosity):
692713
assert_fee_not_in_block(blockhash, 1)
693714
assert_fee_not_in_block(blockhash, True)
694715

716+
self.log.info("Test getblock coinbase metadata fields")
717+
assert_coinbase_metadata(blockhash, 1)
718+
695719
self.log.info('Test that getblock with verbosity 2 and 3 includes expected fee')
696720
assert_fee_in_block(blockhash, 2)
697721
assert_fee_in_block(blockhash, 3)

0 commit comments

Comments
 (0)