mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-04-08 14:47:31 +02:00
Previously the coinbase transaction generated by our miner code was not used downstream, because the getblocktemplate RPC excludes it. Since the Mining IPC interface was introduced in #30200 we do expose this dummy coinbase transaction. In Stratum v2 several parts of it are communicated downstream, including the scriptSig. This commit removes the dummy extraNonce from the coinbase scriptSig in block templates requested via IPC. This limits the scriptSig to what is essential for consensus (BIP34) and removes the need for external mining software to remove the dummy, or even ignore the scriptSig we provide and generate it some other way. This could cause problems if a future soft fork requires additional data to be committed here. A test is added to verify the new IPC behavior. It achieves this by introducing an include_dummy_extranonce option which defaults to false with all test code updated to set it to true. Because this option is not exposed via IPC, callers will no longer see it. The caller needs to ensure that for blocks 1 through 16 they pad the scriptSig in order to avoid bad-cb-length. Co-authored-by: Anthony Towns <aj@erisian.com.au>
147 lines
5.3 KiB
C++
147 lines
5.3 KiB
C++
// Copyright (c) 2019-present The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <test/util/mining.h>
|
|
|
|
#include <chainparams.h>
|
|
#include <consensus/merkle.h>
|
|
#include <consensus/validation.h>
|
|
#include <key_io.h>
|
|
#include <node/context.h>
|
|
#include <pow.h>
|
|
#include <primitives/transaction.h>
|
|
#include <test/util/script.h>
|
|
#include <util/check.h>
|
|
#include <validation.h>
|
|
#include <validationinterface.h>
|
|
#include <versionbits.h>
|
|
|
|
#include <algorithm>
|
|
#include <memory>
|
|
|
|
using node::BlockAssembler;
|
|
using node::NodeContext;
|
|
|
|
COutPoint generatetoaddress(const NodeContext& node, const std::string& address)
|
|
{
|
|
const auto dest = DecodeDestination(address);
|
|
assert(IsValidDestination(dest));
|
|
BlockAssembler::Options assembler_options;
|
|
assembler_options.coinbase_output_script = GetScriptForDestination(dest);
|
|
assembler_options.include_dummy_extranonce = true;
|
|
|
|
return MineBlock(node, assembler_options);
|
|
}
|
|
|
|
std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params)
|
|
{
|
|
std::vector<std::shared_ptr<CBlock>> ret{total_height};
|
|
auto time{params.GenesisBlock().nTime};
|
|
// NOTE: here `height` does not correspond to the block height but the block height - 1.
|
|
for (size_t height{0}; height < total_height; ++height) {
|
|
CBlock& block{*(ret.at(height) = std::make_shared<CBlock>())};
|
|
|
|
CMutableTransaction coinbase_tx;
|
|
coinbase_tx.nLockTime = static_cast<uint32_t>(height);
|
|
coinbase_tx.vin.resize(1);
|
|
coinbase_tx.vin[0].prevout.SetNull();
|
|
coinbase_tx.vin[0].nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; // Make sure timelock is enforced.
|
|
coinbase_tx.vout.resize(1);
|
|
coinbase_tx.vout[0].scriptPubKey = P2WSH_OP_TRUE;
|
|
coinbase_tx.vout[0].nValue = GetBlockSubsidy(height + 1, params.GetConsensus());
|
|
// Always include OP_0 as a dummy extraNonce.
|
|
coinbase_tx.vin[0].scriptSig = CScript() << (height + 1) << OP_0;
|
|
block.vtx = {MakeTransactionRef(std::move(coinbase_tx))};
|
|
|
|
block.nVersion = VERSIONBITS_LAST_OLD_BLOCK_VERSION;
|
|
block.hashPrevBlock = (height >= 1 ? *ret.at(height - 1) : params.GenesisBlock()).GetHash();
|
|
block.hashMerkleRoot = BlockMerkleRoot(block);
|
|
block.nTime = ++time;
|
|
block.nBits = params.GenesisBlock().nBits;
|
|
block.nNonce = 0;
|
|
|
|
while (!CheckProofOfWork(block.GetHash(), block.nBits, params.GetConsensus())) {
|
|
++block.nNonce;
|
|
assert(block.nNonce);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
COutPoint MineBlock(const NodeContext& node, const node::BlockAssembler::Options& assembler_options)
|
|
{
|
|
auto block = PrepareBlock(node, assembler_options);
|
|
auto valid = MineBlock(node, block);
|
|
assert(!valid.IsNull());
|
|
return valid;
|
|
}
|
|
|
|
struct BlockValidationStateCatcher : public CValidationInterface {
|
|
const uint256 m_hash;
|
|
std::optional<BlockValidationState> m_state;
|
|
|
|
BlockValidationStateCatcher(const uint256& hash)
|
|
: m_hash{hash},
|
|
m_state{} {}
|
|
|
|
protected:
|
|
void BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state) override
|
|
{
|
|
if (block->GetHash() != m_hash) return;
|
|
m_state = state;
|
|
}
|
|
};
|
|
|
|
COutPoint MineBlock(const NodeContext& node, std::shared_ptr<CBlock>& block)
|
|
{
|
|
while (!CheckProofOfWork(block->GetHash(), block->nBits, Params().GetConsensus())) {
|
|
++block->nNonce;
|
|
assert(block->nNonce);
|
|
}
|
|
|
|
return ProcessBlock(node, block);
|
|
}
|
|
|
|
COutPoint ProcessBlock(const NodeContext& node, const std::shared_ptr<CBlock>& block)
|
|
{
|
|
auto& chainman{*Assert(node.chainman)};
|
|
const auto old_height = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight());
|
|
bool new_block;
|
|
BlockValidationStateCatcher bvsc{block->GetHash()};
|
|
node.validation_signals->RegisterValidationInterface(&bvsc);
|
|
const bool processed{chainman.ProcessNewBlock(block, true, true, &new_block)};
|
|
const bool duplicate{!new_block && processed};
|
|
assert(!duplicate);
|
|
node.validation_signals->UnregisterValidationInterface(&bvsc);
|
|
node.validation_signals->SyncWithValidationInterfaceQueue();
|
|
const bool was_valid{bvsc.m_state && bvsc.m_state->IsValid()};
|
|
assert(old_height + was_valid == WITH_LOCK(chainman.GetMutex(), return chainman.ActiveHeight()));
|
|
|
|
if (was_valid) return {block->vtx[0]->GetHash(), 0};
|
|
return {};
|
|
}
|
|
|
|
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node,
|
|
const BlockAssembler::Options& assembler_options)
|
|
{
|
|
auto block = std::make_shared<CBlock>(
|
|
BlockAssembler{Assert(node.chainman)->ActiveChainstate(), Assert(node.mempool.get()), assembler_options}
|
|
.CreateNewBlock()
|
|
->block);
|
|
|
|
LOCK(cs_main);
|
|
block->nTime = Assert(node.chainman)->ActiveChain().Tip()->GetMedianTimePast() + 1;
|
|
block->hashMerkleRoot = BlockMerkleRoot(*block);
|
|
|
|
return block;
|
|
}
|
|
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node, const CScript& coinbase_scriptPubKey)
|
|
{
|
|
BlockAssembler::Options assembler_options;
|
|
assembler_options.coinbase_output_script = coinbase_scriptPubKey;
|
|
assembler_options.include_dummy_extranonce = true;
|
|
ApplyArgsManOptions(*node.args, assembler_options);
|
|
return PrepareBlock(node, assembler_options);
|
|
}
|