Files
bitcoin/src/test/fuzz/process_message.cpp
Sjors Provoost d511adb664 [miner] omit dummy extraNonce via IPC
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>
2026-01-27 14:45:35 +01:00

135 lines
4.7 KiB
C++

// Copyright (c) 2020-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 <banman.h>
#include <consensus/consensus.h>
#include <net.h>
#include <net_processing.h>
#include <node/warnings.h>
#include <primitives/transaction.h>
#include <protocol.h>
#include <script/script.h>
#include <sync.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
#include <test/fuzz/util/net.h>
#include <test/util/mining.h>
#include <test/util/net.h>
#include <test/util/setup_common.h>
#include <test/util/validation.h>
#include <util/check.h>
#include <util/time.h>
#include <validationinterface.h>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <string_view>
#include <vector>
namespace {
TestingSetup* g_setup;
std::string_view LIMIT_TO_MESSAGE_TYPE{};
void ResetChainman(TestingSetup& setup)
{
SetMockTime(setup.m_node.chainman->GetParams().GenesisBlock().Time());
setup.m_node.chainman.reset();
setup.m_make_chainman();
setup.LoadVerifyActivateChainstate();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
node::BlockAssembler::Options options;
options.include_dummy_extranonce = true;
MineBlock(setup.m_node, options);
}
setup.m_node.validation_signals->SyncWithValidationInterfaceQueue();
}
} // namespace
void initialize_process_message()
{
if (const auto val{std::getenv("LIMIT_TO_MESSAGE_TYPE")}) {
LIMIT_TO_MESSAGE_TYPE = val;
Assert(std::count(ALL_NET_MESSAGE_TYPES.begin(), ALL_NET_MESSAGE_TYPES.end(), LIMIT_TO_MESSAGE_TYPE)); // Unknown message type passed
}
static const auto testing_setup{
MakeNoLogFileContext<TestingSetup>(
/*chain_type=*/ChainType::REGTEST,
{}),
};
g_setup = testing_setup.get();
ResetChainman(*g_setup);
}
FUZZ_TARGET(process_message, .init = initialize_process_message)
{
SeedRandomStateForTest(SeedRand::ZEROS);
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
auto& node{g_setup->m_node};
auto& connman{static_cast<ConnmanTestMsg&>(*node.connman)};
connman.ResetAddrCache();
connman.ResetMaxOutboundCycle();
auto& chainman{static_cast<TestChainstateManager&>(*node.chainman)};
const auto block_index_size{WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())};
SetMockTime(1610000000); // any time to successfully reset ibd
chainman.ResetIbd();
chainman.DisableNextWrite();
// Reset, so that dangling pointers can be detected by sanitizers.
node.banman.reset();
node.addrman.reset();
node.peerman.reset();
node.addrman = std::make_unique<AddrMan>(*node.netgroupman, /*deterministic=*/true, /*consistency_check_ratio=*/0);
node.peerman = PeerManager::make(connman, *node.addrman,
/*banman=*/nullptr, chainman,
*node.mempool, *node.warnings,
PeerManager::Options{
.reconcile_txs = true,
.deterministic_rng = true,
});
connman.SetMsgProc(node.peerman.get());
connman.SetAddrman(*node.addrman);
LOCK(NetEventsInterface::g_msgproc_mutex);
const std::string random_message_type{fuzzed_data_provider.ConsumeBytesAsString(CMessageHeader::MESSAGE_TYPE_SIZE).c_str()};
if (!LIMIT_TO_MESSAGE_TYPE.empty() && random_message_type != LIMIT_TO_MESSAGE_TYPE) {
return;
}
CNode& p2p_node = *ConsumeNodeAsUniquePtr(fuzzed_data_provider).release();
connman.AddTestNode(p2p_node);
FillNode(fuzzed_data_provider, connman, p2p_node);
const auto mock_time = ConsumeTime(fuzzed_data_provider);
SetMockTime(mock_time);
CSerializedNetMsg net_msg;
net_msg.m_type = random_message_type;
net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider, MAX_PROTOCOL_MESSAGE_LENGTH);
connman.FlushSendBuffer(p2p_node);
(void)connman.ReceiveMsgFrom(p2p_node, std::move(net_msg));
bool more_work{true};
while (more_work) {
p2p_node.fPauseSend = false;
try {
more_work = connman.ProcessMessagesOnce(p2p_node);
} catch (const std::ios_base::failure&) {
}
node.peerman->SendMessages(&p2p_node);
}
node.validation_signals->SyncWithValidationInterfaceQueue();
node.connman->StopNodes();
if (block_index_size != WITH_LOCK(chainman.GetMutex(), return chainman.BlockIndex().size())) {
// Reuse the global chainman, but reset it when it is dirty
ResetChainman(*g_setup);
}
}