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.
This commit is contained in:
3 files changed
+61
No files matched your search
@@ -0,0 +1,8 @@
|
|||||||
|
Updated RPCs
|
||||||
|
------------
|
||||||
|
|
||||||
|
- The `getblock` RPC now returns a `coinbase_tx` object at verbosity levels 1, 2,
|
||||||
|
and 3. It contains `version`, `locktime`, `sequence`, `coinbase` and
|
||||||
|
`witness`. This allows for efficiently querying coinbase
|
||||||
|
transaction properties without fetching the full transaction data at
|
||||||
|
verbosity 2+. (#34512)
|
||||||
@@ -181,6 +181,24 @@ UniValue blockheaderToJSON(const CBlockIndex& tip, const CBlockIndex& blockindex
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Serialize coinbase transaction metadata */
|
||||||
|
UniValue coinbaseTxToJSON(const CTransaction& coinbase_tx)
|
||||||
|
{
|
||||||
|
CHECK_NONFATAL(!coinbase_tx.vin.empty());
|
||||||
|
const CTxIn& vin_0{coinbase_tx.vin[0]};
|
||||||
|
UniValue coinbase_tx_obj(UniValue::VOBJ);
|
||||||
|
coinbase_tx_obj.pushKV("version", coinbase_tx.version);
|
||||||
|
coinbase_tx_obj.pushKV("locktime", coinbase_tx.nLockTime);
|
||||||
|
coinbase_tx_obj.pushKV("sequence", vin_0.nSequence);
|
||||||
|
coinbase_tx_obj.pushKV("coinbase", HexStr(vin_0.scriptSig));
|
||||||
|
const auto& witness_stack{vin_0.scriptWitness.stack};
|
||||||
|
if (!witness_stack.empty()) {
|
||||||
|
CHECK_NONFATAL(witness_stack.size() == 1);
|
||||||
|
coinbase_tx_obj.pushKV("witness", HexStr(witness_stack[0]));
|
||||||
|
}
|
||||||
|
return coinbase_tx_obj;
|
||||||
|
}
|
||||||
|
|
||||||
UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity, const uint256 pow_limit)
|
UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIndex& tip, const CBlockIndex& blockindex, TxVerbosity verbosity, const uint256 pow_limit)
|
||||||
{
|
{
|
||||||
UniValue result = blockheaderToJSON(tip, blockindex, pow_limit);
|
UniValue result = blockheaderToJSON(tip, blockindex, pow_limit);
|
||||||
@@ -188,6 +206,10 @@ UniValue blockToJSON(BlockManager& blockman, const CBlock& block, const CBlockIn
|
|||||||
result.pushKV("strippedsize", ::GetSerializeSize(TX_NO_WITNESS(block)));
|
result.pushKV("strippedsize", ::GetSerializeSize(TX_NO_WITNESS(block)));
|
||||||
result.pushKV("size", ::GetSerializeSize(TX_WITH_WITNESS(block)));
|
result.pushKV("size", ::GetSerializeSize(TX_WITH_WITNESS(block)));
|
||||||
result.pushKV("weight", ::GetBlockWeight(block));
|
result.pushKV("weight", ::GetBlockWeight(block));
|
||||||
|
|
||||||
|
CHECK_NONFATAL(!block.vtx.empty());
|
||||||
|
result.pushKV("coinbase_tx", coinbaseTxToJSON(*block.vtx[0]));
|
||||||
|
|
||||||
UniValue txs(UniValue::VARR);
|
UniValue txs(UniValue::VARR);
|
||||||
txs.reserve(block.vtx.size());
|
txs.reserve(block.vtx.size());
|
||||||
|
|
||||||
@@ -760,6 +782,14 @@ static RPCHelpMan getblock()
|
|||||||
{RPCResult::Type::NUM, "size", "The block size"},
|
{RPCResult::Type::NUM, "size", "The block size"},
|
||||||
{RPCResult::Type::NUM, "strippedsize", "The block size excluding witness data"},
|
{RPCResult::Type::NUM, "strippedsize", "The block size excluding witness data"},
|
||||||
{RPCResult::Type::NUM, "weight", "The block weight as defined in BIP 141"},
|
{RPCResult::Type::NUM, "weight", "The block weight as defined in BIP 141"},
|
||||||
|
{RPCResult::Type::OBJ, "coinbase_tx", "Coinbase transaction metadata",
|
||||||
|
{
|
||||||
|
{RPCResult::Type::NUM, "version", "The coinbase transaction version"},
|
||||||
|
{RPCResult::Type::NUM, "locktime", "The coinbase transaction's locktime (nLockTime)"},
|
||||||
|
{RPCResult::Type::NUM, "sequence", "The coinbase input's sequence number (nSequence)"},
|
||||||
|
{RPCResult::Type::STR_HEX, "coinbase", "The coinbase input's script"},
|
||||||
|
{RPCResult::Type::STR_HEX, "witness", /*optional=*/true, "The coinbase input's first (and only) witness stack element, if present"},
|
||||||
|
}},
|
||||||
{RPCResult::Type::NUM, "height", "The block height or index"},
|
{RPCResult::Type::NUM, "height", "The block height or index"},
|
||||||
{RPCResult::Type::NUM, "version", "The block version"},
|
{RPCResult::Type::NUM, "version", "The block version"},
|
||||||
{RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
|
{RPCResult::Type::STR_HEX, "versionHex", "The block version formatted in hexadecimal"},
|
||||||
|
|||||||
@@ -646,6 +646,26 @@ class BlockchainTest(BitcoinTestFramework):
|
|||||||
self.wallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
|
self.wallet.send_self_transfer(fee_rate=fee_per_kb, from_node=node)
|
||||||
blockhash = self.generate(node, 1)[0]
|
blockhash = self.generate(node, 1)[0]
|
||||||
|
|
||||||
|
def assert_coinbase_metadata(hash, verbosity):
|
||||||
|
block = node.getblock(hash, verbosity)
|
||||||
|
coinbase_tx = node.getblock(hash, 2)["tx"][0]
|
||||||
|
|
||||||
|
expected_keys = {"version", "locktime", "sequence", "coinbase"}
|
||||||
|
if "txinwitness" in coinbase_tx["vin"][0]:
|
||||||
|
expected_keys.add("witness")
|
||||||
|
assert_equal(set(block["coinbase_tx"].keys()), expected_keys)
|
||||||
|
|
||||||
|
assert_equal(block["coinbase_tx"]["version"], coinbase_tx["version"])
|
||||||
|
assert_equal(block["coinbase_tx"]["locktime"], coinbase_tx["locktime"])
|
||||||
|
assert_equal(block["coinbase_tx"]["sequence"], coinbase_tx["vin"][0]["sequence"])
|
||||||
|
assert_equal(block["coinbase_tx"]["coinbase"], coinbase_tx["vin"][0]["coinbase"])
|
||||||
|
|
||||||
|
witness_stack = coinbase_tx["vin"][0].get("txinwitness")
|
||||||
|
if witness_stack is None:
|
||||||
|
assert "witness" not in block["coinbase_tx"]
|
||||||
|
else:
|
||||||
|
assert_equal(block["coinbase_tx"]["witness"], witness_stack[0])
|
||||||
|
|
||||||
def assert_hexblock_hashes(verbosity):
|
def assert_hexblock_hashes(verbosity):
|
||||||
block = node.getblock(blockhash, verbosity)
|
block = node.getblock(blockhash, verbosity)
|
||||||
assert_equal(blockhash, hash256(bytes.fromhex(block[:160]))[::-1].hex())
|
assert_equal(blockhash, hash256(bytes.fromhex(block[:160]))[::-1].hex())
|
||||||
@@ -692,6 +712,9 @@ class BlockchainTest(BitcoinTestFramework):
|
|||||||
assert_fee_not_in_block(blockhash, 1)
|
assert_fee_not_in_block(blockhash, 1)
|
||||||
assert_fee_not_in_block(blockhash, True)
|
assert_fee_not_in_block(blockhash, True)
|
||||||
|
|
||||||
|
self.log.info("Test getblock coinbase metadata fields")
|
||||||
|
assert_coinbase_metadata(blockhash, 1)
|
||||||
|
|
||||||
self.log.info('Test that getblock with verbosity 2 and 3 includes expected fee')
|
self.log.info('Test that getblock with verbosity 2 and 3 includes expected fee')
|
||||||
assert_fee_in_block(blockhash, 2)
|
assert_fee_in_block(blockhash, 2)
|
||||||
assert_fee_in_block(blockhash, 3)
|
assert_fee_in_block(blockhash, 3)
|
||||||
|
|||||||
Reference in new issue
Block a user