Skip to content

Commit 326789a

Browse files
committed
mining: make non-mandatory coinbase commitment opt-in
Previously the dummy coinbase returned by the Mining IPC interface always had a witness and SegWit OP_RETURN output. This is unnecessary for empty blocks as well as for blocks without any SegWit spends. This commit makes this behavior configurable via always_add_coinbase_commitment on BlockCreateOptions. It defaults to false, making this a potentially breaking change to clients. The presence of this field in requests makes it incompatible with Bitcoin Core v30, so it's a breaking change in any case. Preserve historical getblocktemplate RPC behavior. Although it doesn't return a coinbase transaction, it does always provide a default_witness_commitment. This is set in BlockAssembler::CreateNewBlock() via a call GenerateCoinbaseCommitment(), which has the side-effect of adding a coinbase witness.
1 parent 1dae533 commit 326789a

File tree

7 files changed

+35
-3
lines changed

7 files changed

+35
-3
lines changed

src/ipc/capnp/mining.capnp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ struct BlockCreateOptions $Proxy.wrap("node::BlockCreateOptions") {
3939
useMempool @0 :Bool $Proxy.name("use_mempool");
4040
blockReservedWeight @1 :UInt64 $Proxy.name("block_reserved_weight");
4141
coinbaseOutputMaxAdditionalSigops @2 :UInt64 $Proxy.name("coinbase_output_max_additional_sigops");
42+
alwaysAddCoinbaseCommitment @3 :Bool $Proxy.name("always_add_coinbase_commitment");
4243
}
4344

4445
struct BlockWaitOptions $Proxy.wrap("node::BlockWaitOptions") {

src/node/miner.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,12 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
169169
Assert(nHeight > 0);
170170
coinbaseTx.nLockTime = static_cast<uint32_t>(nHeight - 1);
171171
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
172-
pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
172+
if (m_options.always_add_coinbase_commitment || std::any_of(
173+
pblock->vtx.begin(), pblock->vtx.end(),
174+
[&](const CTransactionRef tx) { return tx->HasWitness(); }
175+
)) {
176+
pblocktemplate->vchCoinbaseCommitment = m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
177+
}
173178

174179
LogPrintf("CreateNewBlock(): block weight: %u txs: %u fees: %ld sigops %d\n", GetBlockWeight(*pblock), nBlockTx, nFees, nBlockSigOpsCost);
175180

src/node/types.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,14 @@ struct BlockCreateOptions {
6464
* coinbase_max_additional_weight and coinbase_output_max_additional_sigops.
6565
*/
6666
CScript coinbase_output_script{CScript() << OP_TRUE};
67+
68+
/*
69+
* Whether blocks without SegWit transactions (e.g. empty blocks) should
70+
* have a SegWit commitment, i.e. the coinbase witness and OP_RETURN.
71+
*
72+
* @note IPC clients should omit this field for compatibility with v30.0
73+
*/
74+
bool always_add_coinbase_commitment{false};
6775
};
6876

6977
struct BlockWaitOptions {

src/rpc/mining.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ static RPCHelpMan getblocktemplate()
871871
time_start = GetTime();
872872

873873
// Create new block
874-
block_template = miner.createNewBlock();
874+
block_template = miner.createNewBlock({.always_add_coinbase_commitment = true});
875875
CHECK_NONFATAL(block_template);
876876

877877

src/test/fuzz/utxo_total_supply.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ FUZZ_TARGET(utxo_total_supply)
4545
};
4646
BlockAssembler::Options options;
4747
options.coinbase_output_script = CScript() << OP_FALSE;
48+
options.always_add_coinbase_commitment = true;
4849
const auto PrepareNextBlock = [&]() {
4950
// Use OP_FALSE to avoid BIP30 check from hitting early
5051
auto block = PrepareBlock(node, options);

src/test/util/setup_common.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,7 @@ CBlock TestChain100Setup::CreateBlock(
404404
{
405405
BlockAssembler::Options options;
406406
options.coinbase_output_script = scriptPubKey;
407+
options.always_add_coinbase_commitment = true;
407408
CBlock block = BlockAssembler{chainstate, nullptr, options}.CreateNewBlock()->block;
408409

409410
Assert(block.vtx.size() == 1);

test/functional/interface_ipc.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ async def async_routine():
141141
assert_equal(len(header.result), block_header_size)
142142
block = await self.parse_and_deserialize_block(template, ctx)
143143
assert_equal(ser_uint256(block.hashPrevBlock), newblockref.result.hash)
144-
assert len(block.vtx) >= 1
144+
# Template block.vtx only contains a (dummy) coinbase
145+
assert_equal(len(block.vtx), 1)
145146
txfees = await template.result.getTxFees(ctx)
146147
assert_equal(len(txfees.result), 0)
147148
txsigops = await template.result.getTxSigops(ctx)
@@ -150,6 +151,19 @@ async def async_routine():
150151
coinbase = CTransaction()
151152
coinbase.deserialize(coinbase_data)
152153
assert_equal(coinbase.vin[0].prevout.hash, 0)
154+
# Check that coinbase for an empty block doesn't have a witness
155+
assert(coinbase.wit.is_null())
156+
# Inspect coinbase template returned by getCoinbase()
157+
# coinbase_template = (await template.result.getCoinbase(ctx)).result
158+
# assert(not coinbase_template._has('witness'))
159+
160+
# Unless requested otherwise
161+
opts.alwaysAddCoinbaseCommitment = True
162+
template = mining.result.createNewBlock(ctx, opts)
163+
coinbase_data = BytesIO((await template.result.getCoinbaseTx(ctx)).result)
164+
coinbase.deserialize(coinbase_data)
165+
assert(not coinbase.wit.is_null())
166+
153167
self.log.debug("Wait for a new template")
154168
waitoptions = self.capnp_modules['mining'].BlockWaitOptions()
155169
waitoptions.timeout = timeout
@@ -180,6 +194,8 @@ async def async_routine():
180194
template6 = await waitnext
181195
block4 = await self.parse_and_deserialize_block(template6, ctx)
182196
assert_equal(len(block4.vtx), 3)
197+
# Check that coinbase for non-empty block has a witness")
198+
assert(not block4.vtx[0].wit.is_null())
183199
self.log.debug("Wait for another, but time out, since the fee threshold is set now")
184200
template7 = await template6.result.waitNext(ctx, waitoptions)
185201
assert_equal(template7.to_dict(), {})

0 commit comments

Comments
 (0)