Skip to content

Commit d31cbc4

Browse files
committed
mining: expose OP_RETURN signals in required_outputs
1 parent 5fa2946 commit d31cbc4

File tree

5 files changed

+66
-4
lines changed

5 files changed

+66
-4
lines changed

src/node/miner.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
#include <policy/policy.h>
2323
#include <pow.h>
2424
#include <primitives/transaction.h>
25+
#include <script/script.h>
2526
#include <util/moneystr.h>
2627
#include <util/signalinterrupt.h>
2728
#include <util/time.h>
2829
#include <validation.h>
30+
#include <versionbits.h>
2931

3032
#include <algorithm>
3133
#include <utility>
@@ -196,6 +198,13 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
196198
coinbaseTx.nLockTime = static_cast<uint32_t>(nHeight - 1);
197199
coinbase_tx.lock_time = coinbaseTx.nLockTime;
198200

201+
if ((pblock->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS &&
202+
(pblock->nVersion & VERSIONBITS_DEPLOYMENT_OPRETURN_FLAG) != 0) {
203+
for (const auto& signal_tag : m_chainstate.m_chainman.m_versionbitscache.GetRequiredSignalTags(pindexPrev, chainparams.GetConsensus())) {
204+
coinbase_tx.required_outputs.emplace_back(0, CScript{} << OP_RETURN << std::vector<unsigned char>{signal_tag.begin(), signal_tag.end()});
205+
}
206+
}
207+
199208
pblock->vtx[0] = MakeTransactionRef(std::move(coinbaseTx));
200209
m_chainstate.m_chainman.GenerateCoinbaseCommitment(*pblock, pindexPrev);
201210

src/node/types.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,8 @@ struct CoinbaseTx {
149149
CAmount block_reward_remaining;
150150
/*
151151
* To be included as the last outputs in the coinbase transaction.
152-
* Currently this is only the witness commitment OP_RETURN, but future
153-
* softforks or a custom mining patch could add more.
152+
* Currently this includes the witness commitment OP_RETURN, and may also
153+
* include other zero-value signalling outputs for supported softforks.
154154
*
155155
* The dummy output that spends the full reward is excluded.
156156
*/

src/versionbits.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,20 @@ int32_t VersionBitsCache::ComputeBlockVersion(const CBlockIndex* pindexPrev, con
347347
return ::ComputeBlockVersion(pindexPrev, params, m_caches);
348348
}
349349

350+
std::vector<std::string> VersionBitsCache::GetRequiredSignalTags(const CBlockIndex* pindexPrev, const Consensus::Params& params)
351+
{
352+
LOCK(m_mutex);
353+
354+
std::vector<std::string> signal_tags;
355+
ForEachSignallingDeployment(pindexPrev, params, m_caches, [&](Consensus::DeploymentPos pos, const VersionBitsConditionChecker&) {
356+
const auto& signal_tag{params.vDeployments[pos].signal_tag};
357+
if (!signal_tag.empty()) {
358+
signal_tags.push_back(signal_tag);
359+
}
360+
});
361+
return signal_tags;
362+
}
363+
350364
void VersionBitsCache::Clear()
351365
{
352366
LOCK(m_mutex);

src/versionbits.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <array>
1212
#include <map>
1313
#include <optional>
14+
#include <string>
1415
#include <vector>
1516

1617
class CChainParams;
@@ -97,6 +98,9 @@ class VersionBitsCache
9798
/** Determine what nVersion a new block should use */
9899
int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
99100

101+
/** Return the OP_RETURN deployment tags required in the next block's coinbase. */
102+
std::vector<std::string> GetRequiredSignalTags(const CBlockIndex* pindexPrev, const Consensus::Params& params) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);
103+
100104
/** Check for unknown activations
101105
* Returns a vector containing the bit number used for signalling and a bool
102106
* indicating the deployment is likely to be ACTIVE, rather than merely LOCKED_IN. */

test/functional/interface_ipc_mining.py

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
from test_framework.script import (
2525
CScript,
2626
CScriptNum,
27+
OP_RETURN,
2728
)
2829
from test_framework.test_framework import BitcoinTestFramework
2930
from test_framework.util import (
@@ -96,8 +97,8 @@ async def build_coinbase_test(self, template, ctx, miniwallet):
9697
coinbase_tx.vout = [CTxOut()]
9798
coinbase_tx.vout[0].scriptPubKey = miniwallet.get_output_script()
9899
coinbase_tx.vout[0].nValue = coinbase_res.blockRewardRemaining
99-
# Add SegWit OP_RETURN. This is currently always present even for
100-
# empty blocks, but this may change.
100+
# Append any required coinbase outputs, such as the witness
101+
# commitment and deployment signalling OP_RETURNs.
101102
for output_data in coinbase_res.requiredOutputs:
102103
output = CTxOut()
103104
output.deserialize(BytesIO(output_data))
@@ -396,6 +397,39 @@ async def async_routine():
396397

397398
asyncio.run(capnp.run(async_routine()))
398399

400+
def run_opreturn_required_outputs_test(self):
401+
self.log.info("Running OP_RETURN required_outputs test")
402+
self.restart_node(0, extra_args=[
403+
'-vbparams=testdummy:999999999999:999999999999',
404+
'-vbparams=testdummy2:0:999999999999',
405+
])
406+
407+
async def async_routine():
408+
ctx, mining = await self.make_mining_ctx()
409+
410+
async with AsyncExitStack() as stack:
411+
template = await mining_create_block_template(mining, stack, ctx, self.default_block_create_options)
412+
assert template is not None
413+
414+
coinbase_res = await mining_get_coinbase_tx(template, ctx)
415+
assert_equal(len(coinbase_res.requiredOutputs), 2)
416+
417+
signal_output = CTxOut()
418+
signal_output.deserialize(BytesIO(coinbase_res.requiredOutputs[0]))
419+
assert_equal(signal_output.nValue, 0)
420+
assert_equal(signal_output.scriptPubKey.hex(), CScript([OP_RETURN, b"BIP-9999"]).hex())
421+
422+
witness_output = CTxOut()
423+
witness_output.deserialize(BytesIO(coinbase_res.requiredOutputs[1]))
424+
assert_equal(witness_output.nValue, 0)
425+
# getblocktemplate only exposes the witness commitment today.
426+
assert_equal(
427+
witness_output.scriptPubKey.hex(),
428+
self.nodes[0].getblocktemplate({"rules": ["segwit"]})["default_witness_commitment"],
429+
)
430+
431+
asyncio.run(capnp.run(async_routine()))
432+
399433
def run_test(self):
400434
self.miniwallet = MiniWallet(self.nodes[0])
401435
self.default_block_create_options = self.capnp_modules['mining'].BlockCreateOptions()
@@ -404,6 +438,7 @@ def run_test(self):
404438
self.run_block_template_test()
405439
self.run_coinbase_and_submission_test()
406440
self.run_ipc_option_override_test()
441+
self.run_opreturn_required_outputs_test()
407442

408443

409444
if __name__ == '__main__':

0 commit comments

Comments
 (0)