Merge bitcoin/bitcoin#33966: refactor: disentangle miner startup defaults from runtime options

1e5d3b4f0d doc: add release note for mining option validation (Sjors Provoost)
0317f52022 ci: enforce iwyu for touched files (Sjors Provoost)
8c58f63578 refactor: have mining files include what they use (Sjors Provoost)
3bb6498fb0 mining: store block create options in NodeContext (Sjors Provoost)
4637cd157d mining: reject invalid block create options (Sjors Provoost)
8daac1d6eb mining: add block create option helpers (Sjors Provoost)
128da7c3ff miner: add block_max_weight to BlockCreateOptions (Sjors Provoost)
fa81e51eae mining: parse block creation args in mining_args (Sjors Provoost)
020166080c mining: use interface for tests, bench and fuzzers (Sjors Provoost)
44082bea47 interfaces: make Mining use const NodeContext (Sjors Provoost)
d4368e059c move-only: add node/mining_types.h (Sjors Provoost)
6aeb1fbea2 test: cover IPC blockmaxweight policy (Sjors Provoost)
63b23ea1e9 test: regression test for waitNext mining policy (Sjors Provoost)
24750f8b31 test: add createNewBlock failure helper (Sjors Provoost)
63ee9cd15b test: misc interface_ipc_mining.py improvements (Sjors Provoost)

Pull request description:

  Although this PR is primarily a refactor, _there are behavior changes_ documented in the release note:
  - the IPC mining interface now rejects out-of-range block template options instead of silently clamping them;
  - startup now rejects `-blockmaxweight` values lower than `-blockreservedweight`, instead of allowing them to be clamped later.

  The interaction between node startup options like `-blockreservedweight` and runtime options, especially those passed via IPC, is confusing.

  They're combined in `BlockAssembler::Options`, which this PR gets rid of in favour of `BlockCreateOptions`.

  `BlockCreateOptions` is used by interface clients. As before, IPC clients have access to a safe / sane subset, whereas RPC and test code can use all fields. The same type is also used to store mining defaults parsed once during node startup in `NodeContext`.

  The maximum block weight setting (`block_max_weight`) is optional. When read from startup options it matches `-blockmaxweight`; when provided by callers it is a runtime override. `Merge()` fills unset fields from startup defaults while preserving caller-provided values.

  This all happens in commits `mining: add block create option helpers` and `mining: store block create options in NodeContext`, and requires some preparation to keep things easy to review.

  We get rid of `BlockAssembler::Options` but this is used in many tests. Since large churn is inevitable, we might as well switch all tests, bench and fuzzers over to the Mining interface. The `mining: use interface for tests, bench and fuzzers` commit does that, dramatically reducing direct use of `BlockAssembler`. Two exceptions are documented in the commit message. Because `test_block_validity` wasn't available via the interface and the block_assemble benchmark needs it, it's moved from `BlockAssembler::Options` to `BlockCreateOptions` (still not exposed via IPC).

  We need access to mining related structs from both the miner and node initialization code. To avoid having to pull in all of `BlockAssembler` for the latter, the `move-only: add node/mining_types.h` commit introduces `node/mining_types.h` and moves `BlockCreateOptions`, `BlockWaitOptions` and `BlockCheckOptions` there from `src/node/types.h`.

  I considered also moving `DEFAULT_BLOCK_MAX_WEIGHT`, `DEFAULT_BLOCK_RESERVED_WEIGHT`, `MINIMUM_BLOCK_RESERVED_WEIGHT` and `DEFAULT_BLOCK_MIN_TX_FEE` there from `policy.h`, since they are distinct from relay policy and not needed by the kernel. But this seems more appropriate for a follow-up and requires additional discussion.

  ---

  I kept variable renaming and other formatting changes to a minimum to ease review with `--color-moved=dimmed-zebra`.

  ## Commit summary

  Tests and test cleanup:
  - `test: misc interface_ipc_mining.py improvements`
  - `test: add assert_create_fails helper`
  - `test: regression test for waitNext mining policy`
  - `test: cover IPC blockmaxweight policy`

  Refactoring test/bench/fuzz callers:
  - `interfaces: make Mining use const NodeContext`
  - `mining: use interface for tests, bench and fuzzers`

  Moving mining interface types:
  - `move-only: add node/mining_types.h`

  Separating startup defaults from runtime options:
  - `mining: parse block creation args in mining_args`: adds `node/mining_args.{h,cpp}` and moves mining option parsing out of `init.cpp`, without storing the parsed values yet.
  - `miner: add block_max_weight to BlockCreateOptions`: moves the runtime maximum block weight setting into `BlockCreateOptions` as an optional value, so it can later be defaulted from startup args when unset.
  - `mining: add block create option helpers`: centralizes block template option defaulting and merging, removes `BlockAssembler::Options`, and preserves behavior except for dropping the `Specified ` prefix from startup option error messages.
  - `mining: reject invalid block create options`: checks typed `BlockCreateOptions` before block template creation, so invalid runtime options are rejected instead of silently clamped. Startup validation also rejects `-blockmaxweight` values lower than `-blockreservedweight`.
  - `mining: store block create options in NodeContext`: stores the startup mining options in `NodeContext` as `BlockCreateOptions`, so startup defaults and runtime overrides can be merged with the same option type.

  Include hygiene, CI and release note:
  - `refactor: have mining files include what they use`
  - `ci: enforce iwyu for touched files`
  - `doc: add release note for mining option validation`

ACKs for top commit:
  w0xlt:
    reACK 1e5d3b4f0d
  sedited:
    ACK 1e5d3b4f0d
  ryanofsky:
    Code review ACK 1e5d3b4f0d. Looks good, thanks for the updates!

Tree-SHA512: 28c715023cb78f02775caa787b243c994bd0f8ce4559afc8db9301e93400ebbc74963626a4afe65ae15bcc16b9192d051a745839f4c804848d50746ea5a224b4
This commit is contained in:
Ryan Ofsky
2026-05-26 08:14:01 -04:00
40 changed files with 1118 additions and 505 deletions

View File

@@ -229,7 +229,7 @@ fi
if [[ "${RUN_IWYU}" == true ]]; then
# TODO: Consider enforcing IWYU across the entire codebase.
FILES_WITH_ENFORCED_IWYU="/src/(((crypto|index|kernel|primitives|univalue/(lib|test)|util|zmq)/.*|common/license_info|node/blockstorage|node/utxo_snapshot|clientversion|core_io|signet)\\.cpp)"
FILES_WITH_ENFORCED_IWYU="/src/(((crypto|index|kernel|primitives|univalue/(lib|test)|util|zmq)/.*|bench/(block_assemble|connectblock)|common/license_info|node/(blockstorage|interfaces|miner|mining_args|utxo_snapshot)|rpc/mining|clientversion|core_io|signet|init)\\.cpp)"
jq --arg patterns "$FILES_WITH_ENFORCED_IWYU" 'map(select(.file | test($patterns)))' "${BASE_BUILD_DIR}/compile_commands.json" > "${BASE_BUILD_DIR}/compile_commands_iwyu_errors.json"
jq --arg patterns "$FILES_WITH_ENFORCED_IWYU" 'map(select(.file | test($patterns) | not))' "${BASE_BUILD_DIR}/compile_commands.json" > "${BASE_BUILD_DIR}/compile_commands_iwyu_warnings.json"

View File

@@ -0,0 +1,9 @@
Mining
------
- The IPC mining interface now rejects out-of-range block template options
instead of silently clamping them, such as oversized reserved block weight or
coinbase sigops limits. (#33966)
- The `-blockmaxweight` startup option is now rejected when it is lower than
`-blockreservedweight`, instead of being silently clamped. (#33966)

View File

@@ -232,6 +232,7 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL
node/mempool_persist.cpp
node/mempool_persist_args.cpp
node/miner.cpp
node/mining_args.cpp
node/mini_miner.cpp
node/minisketchwrapper.cpp
node/peerman_args.cpp

View File

@@ -4,7 +4,7 @@
#include <bench/bench.h>
#include <consensus/consensus.h>
#include <node/miner.h>
#include <node/mining_types.h>
#include <primitives/transaction.h>
#include <random.h>
#include <script/script.h>
@@ -18,9 +18,10 @@
#include <cassert>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
using node::BlockAssembler;
using node::BlockCreateOptions;
static void AssembleBlock(benchmark::Bench& bench)
{
@@ -28,8 +29,9 @@ static void AssembleBlock(benchmark::Bench& bench)
CScriptWitness witness;
witness.stack.push_back(WITNESS_STACK_ELEM_OP_TRUE);
BlockAssembler::Options options;
options.coinbase_output_script = P2WSH_OP_TRUE;
BlockCreateOptions options{
.coinbase_output_script = P2WSH_OP_TRUE,
};
// Collect some loose transactions that spend the coinbases of our mined blocks
constexpr size_t NUM_BLOCKS{200};
@@ -60,12 +62,12 @@ static void BlockAssemblerAddPackageTxns(benchmark::Bench& bench)
FastRandomContext det_rand{true};
auto testing_setup{MakeNoLogFileContext<TestChain100Setup>()};
testing_setup->PopulateMempool(det_rand, /*num_transactions=*/1000, /*submit=*/true);
BlockAssembler::Options assembler_options;
assembler_options.test_block_validity = false;
assembler_options.coinbase_output_script = P2WSH_OP_TRUE;
bench.run([&] {
PrepareBlock(testing_setup->m_node, assembler_options);
PrepareBlock(testing_setup->m_node, {
.coinbase_output_script = P2WSH_OP_TRUE,
.test_block_validity = false
});
});
}

View File

@@ -4,14 +4,27 @@
#include <addresstype.h>
#include <bench/bench.h>
#include <interfaces/chain.h>
#include <kernel/cs_main.h>
#include <script/interpreter.h>
#include <chain.h>
#include <coins.h>
#include <consensus/amount.h>
#include <consensus/validation.h>
#include <key.h>
#include <node/blockstorage.h>
#include <policy/feerate.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <pubkey.h>
#include <script/script.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <validation.h>
#include <cassert>
#include <cstddef>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
/*
@@ -39,7 +52,7 @@ CBlock CreateTestBlock(
{COutPoint(coinbase_to_spend->GetHash(), 0)},
chainstate.m_chain.Height() + 1, keys, outputs, {}, {})};
const CScript coinbase_spk{GetScriptForDestination(coinbase_taproot)};
test_setup.CreateAndProcessBlock({first_tx}, coinbase_spk, &chainstate);
test_setup.CreateAndProcessBlock({first_tx}, coinbase_spk);
std::vector<CMutableTransaction> txs;
txs.reserve(num_txs);
@@ -59,7 +72,7 @@ CBlock CreateTestBlock(
}
// Coinbase output can use any output type as it is not spent and will not change the benchmark
return test_setup.CreateBlock(txs, coinbase_spk, chainstate);
return test_setup.CreateBlock(txs, coinbase_spk);
}
/*

View File

@@ -9,6 +9,7 @@
#include <kernel/checks.h>
#include <addrdb.h>
#include <addrman.h>
#include <banman.h>
#include <blockfilter.h>
@@ -18,13 +19,15 @@
#include <chainparamsbase.h>
#include <clientversion.h>
#include <common/args.h>
#include <common/messages.h>
#include <common/system.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <deploymentstatus.h>
#include <hash.h>
#include <compat/compat.h>
#include <consensus/params.h>
#include <crypto/hex_base.h>
#include <dbwrapper.h>
#include <httprpc.h>
#include <httpserver.h>
#include <index/base.h>
#include <index/blockfilterindex.h>
#include <index/coinstatsindex.h>
#include <index/txindex.h>
@@ -36,14 +39,18 @@
#include <interfaces/mining.h>
#include <interfaces/node.h>
#include <ipc/exception.h>
#include <kernel/blockmanager_opts.h>
#include <kernel/caches.h>
#include <kernel/chainstatemanager_opts.h>
#include <kernel/context.h>
#include <kernel/notifications_interface.h>
#include <key.h>
#include <logging.h>
#include <mapport.h>
#include <net.h>
#include <net_permissions.h>
#include <net_processing.h>
#include <netaddress.h>
#include <netbase.h>
#include <netgroup.h>
#include <node/blockmanager_args.h>
@@ -57,7 +64,8 @@
#include <node/mempool_args.h>
#include <node/mempool_persist.h>
#include <node/mempool_persist_args.h>
#include <node/miner.h>
#include <node/mining_args.h>
#include <node/mining_types.h>
#include <node/peerman_args.h>
#include <policy/feerate.h>
#include <policy/fees/block_policy_estimator.h>
@@ -65,19 +73,20 @@
#include <policy/policy.h>
#include <policy/settings.h>
#include <protocol.h>
#include <rpc/blockchain.h>
#include <random.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <rpc/util.h>
#include <scheduler.h>
#include <script/sigcache.h>
#include <sync.h>
#include <tinyformat.h>
#include <torcontrol.h>
#include <txdb.h>
#include <txgraph.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/asmap.h>
#include <util/batchpriority.h>
#include <util/byte_units.h>
#include <util/chaintype.h>
#include <util/check.h>
#include <util/fs.h>
@@ -97,21 +106,31 @@
#include <walletinitinterface.h>
#include <algorithm>
#include <any>
#include <cerrno>
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <cstdio>
#include <exception>
#include <fstream>
#include <functional>
#include <initializer_list>
#include <list>
#include <memory>
#include <new>
#include <optional>
#include <set>
#include <span>
#include <string>
#include <system_error>
#include <thread>
#include <tuple>
#include <utility>
#include <variant>
#include <vector>
#ifndef WIN32
#include <csignal>
#include <sys/stat.h>
#endif
#ifdef ENABLE_ZMQ
@@ -124,7 +143,6 @@
#include <node/data/ip_asn.dat.h>
#endif
using common::AmountErrMsg;
using common::InvalidPortErrMsg;
using common::ResolveErrMsg;
@@ -1074,27 +1092,9 @@ bool AppInitParameterInteraction(const ArgsManager& args)
return InitError(Untranslated("peertimeout must be a positive integer."));
}
if (const auto arg{args.GetArg("-blockmintxfee")}) {
if (!ParseMoney(*arg)) {
return InitError(AmountErrMsg("blockmintxfee", *arg));
}
}
{
const auto max_block_weight = args.GetIntArg("-blockmaxweight", DEFAULT_BLOCK_MAX_WEIGHT);
if (max_block_weight > MAX_BLOCK_WEIGHT) {
return InitError(strprintf(_("Specified -blockmaxweight (%d) exceeds consensus maximum block weight (%d)"), max_block_weight, MAX_BLOCK_WEIGHT));
}
}
{
const auto block_reserved_weight = args.GetIntArg("-blockreservedweight", DEFAULT_BLOCK_RESERVED_WEIGHT);
if (block_reserved_weight > MAX_BLOCK_WEIGHT) {
return InitError(strprintf(_("Specified -blockreservedweight (%d) exceeds consensus maximum block weight (%d)"), block_reserved_weight, MAX_BLOCK_WEIGHT));
}
if (block_reserved_weight < MINIMUM_BLOCK_RESERVED_WEIGHT) {
return InitError(strprintf(_("Specified -blockreservedweight (%d) is lower than minimum safety value of (%d)"), block_reserved_weight, MINIMUM_BLOCK_RESERVED_WEIGHT));
}
auto mining_result{node::ReadMiningArgs(args)};
if (!mining_result) {
return InitError(util::ErrorString(mining_result));
}
nBytesPerSigOp = args.GetIntArg("-bytespersigop", nBytesPerSigOp);
@@ -1328,6 +1328,9 @@ static ChainstateLoadResult InitAndLoadChainstate(
if (!mempool_error.empty()) {
return {ChainstateLoadStatus::FAILURE_FATAL, mempool_error};
}
auto mining_args{node::ReadMiningArgs(args)};
Assert(mining_args); // no error can happen, already checked in AppInitParameterInteraction
node.mining_args = std::move(*mining_args);
LogInfo("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)",
cache_sizes.coins / double(1_MiB),
mempool_opts.max_size_bytes / double(1_MiB));

View File

@@ -7,7 +7,7 @@
#include <consensus/amount.h>
#include <interfaces/types.h>
#include <node/types.h>
#include <node/mining_types.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <uint256.h>
@@ -16,15 +16,13 @@
#include <cstdint>
#include <memory>
#include <optional>
#include <string>
#include <vector>
namespace node {
struct NodeContext;
} // namespace node
class BlockValidationState;
class CScript;
namespace interfaces {
//! Block template interface
@@ -161,7 +159,7 @@ public:
//! Get internal node context. Useful for RPC and testing,
//! but not accessible across processes.
virtual node::NodeContext* context() { return nullptr; }
virtual const node::NodeContext* context() { return nullptr; }
};
//! Return implementation of Mining interface.
@@ -169,7 +167,7 @@ public:
//! @param[in] wait_loaded waits for chainstate data to be loaded before
//! returning. Used to prevent external clients from
//! being able to crash the node during startup.
std::unique_ptr<Mining> MakeMining(node::NodeContext& node, bool wait_loaded=true);
std::unique_ptr<Mining> MakeMining(const node::NodeContext& node, bool wait_loaded=true);
} // namespace interfaces

View File

@@ -5,6 +5,8 @@
#ifndef BITCOIN_NODE_CONTEXT_H
#define BITCOIN_NODE_CONTEXT_H
#include <node/mining_types.h>
#include <atomic>
#include <cstdlib>
#include <functional>
@@ -81,6 +83,11 @@ struct NodeContext {
//! Reference to chain client that should used to load or create wallets
//! opened by the gui.
std::unique_ptr<interfaces::Mining> mining;
//! Mining options used to create block templates. This value member is an
//! exception to the dependency guidance above because BlockCreateOptions is
//! a minimal dependency. It could be moved to the BlockTemplateCache
//! proposed in bitcoin/bitcoin#33421.
BlockCreateOptions mining_args;
interfaces::WalletLoader* wallet_loader{nullptr};
std::unique_ptr<CScheduler> scheduler;
std::function<void()> rpc_interruption_point = [] {};

View File

@@ -2,16 +2,19 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrdb.h>
#include <bitcoin-build-config.h> // IWYU pragma: keep
#include <banman.h>
#include <blockfilter.h>
#include <btcsignals.h>
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
#include <common/args.h>
#include <common/settings.h>
#include <consensus/amount.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <deploymentstatus.h>
#include <external_signer.h>
#include <httprpc.h>
#include <index/blockfilterindex.h>
@@ -22,23 +25,24 @@
#include <interfaces/node.h>
#include <interfaces/rpc.h>
#include <interfaces/types.h>
#include <interfaces/wallet.h>
#include <kernel/chain.h>
#include <kernel/context.h>
#include <kernel/mempool_entry.h>
#include <key.h>
#include <logging.h>
#include <mapport.h>
#include <net.h>
#include <net_processing.h>
#include <net_types.h>
#include <netaddress.h>
#include <netbase.h>
#include <node/blockstorage.h>
#include <node/coin.h>
#include <node/context.h>
#include <node/interface_ui.h>
#include <node/mini_miner.h>
#include <node/miner.h>
#include <node/kernel_notifications.h>
#include <node/miner.h>
#include <node/mini_miner.h>
#include <node/mining_args.h>
#include <node/mining_types.h>
#include <node/transaction.h>
#include <node/types.h>
#include <node/warnings.h>
@@ -46,13 +50,12 @@
#include <policy/fees/block_policy_estimator.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <policy/settings.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <rpc/blockchain.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <rpc/server.h>
#include <support/allocators/secure.h>
#include <sync.h>
#include <txmempool.h>
#include <uint256.h>
@@ -61,17 +64,24 @@
#include <util/result.h>
#include <util/signalinterrupt.h>
#include <util/string.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <bitcoin-build-config.h> // IWYU pragma: keep
#include <any>
#include <atomic>
#include <condition_variable>
#include <cstdint>
#include <cstdlib>
#include <functional>
#include <map>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
using interfaces::BlockRef;
using interfaces::BlockTemplate;
@@ -86,6 +96,7 @@ using interfaces::Rpc;
using interfaces::WalletLoader;
using kernel::ChainstateRole;
using node::BlockAssembler;
using node::BlockCreateOptions;
using node::BlockWaitOptions;
using node::CoinbaseTx;
using util::Join;
@@ -867,11 +878,11 @@ public:
class BlockTemplateImpl : public BlockTemplate
{
public:
explicit BlockTemplateImpl(BlockAssembler::Options assemble_options,
explicit BlockTemplateImpl(BlockCreateOptions create_options,
std::unique_ptr<CBlockTemplate> block_template,
NodeContext& node) : m_assemble_options(std::move(assemble_options)),
m_block_template(std::move(block_template)),
m_node(node)
const NodeContext& node) : m_create_options(std::move(create_options)),
m_block_template(std::move(block_template)),
m_node(node)
{
assert(m_block_template);
}
@@ -914,8 +925,14 @@ public:
std::unique_ptr<BlockTemplate> waitNext(BlockWaitOptions options) override
{
auto new_template = WaitAndCreateNewBlock(chainman(), notifications(), m_node.mempool.get(), m_block_template, options, m_assemble_options, m_interrupt_wait);
if (new_template) return std::make_unique<BlockTemplateImpl>(m_assemble_options, std::move(new_template), m_node);
auto new_template = WaitAndCreateNewBlock(chainman(),
notifications(),
m_node.mempool.get(),
m_block_template,
/*wait_options=*/options,
/*create_options=*/m_create_options,
/*interrupt_wait=*/m_interrupt_wait);
if (new_template) return std::make_unique<BlockTemplateImpl>(m_create_options, std::move(new_template), m_node);
return nullptr;
}
@@ -924,20 +941,20 @@ public:
InterruptWait(notifications(), m_interrupt_wait);
}
const BlockAssembler::Options m_assemble_options;
const BlockCreateOptions m_create_options;
const std::unique_ptr<CBlockTemplate> m_block_template;
bool m_interrupt_wait{false};
ChainstateManager& chainman() { return *Assert(m_node.chainman); }
KernelNotifications& notifications() { return *Assert(m_node.notifications); }
NodeContext& m_node;
const NodeContext& m_node;
};
class MinerImpl : public Mining
{
public:
explicit MinerImpl(NodeContext& node) : m_node(node) {}
explicit MinerImpl(const NodeContext& node) : m_node(node) {}
bool isTestChain() override
{
@@ -961,16 +978,6 @@ public:
std::unique_ptr<BlockTemplate> createNewBlock(const BlockCreateOptions& options, bool cooldown) override
{
// Reject too-small values instead of clamping so callers don't silently
// end up mining with different options than requested. This matches the
// behavior of the `-blockreservedweight` startup option, which rejects
// values below MINIMUM_BLOCK_RESERVED_WEIGHT.
if (options.block_reserved_weight && options.block_reserved_weight < MINIMUM_BLOCK_RESERVED_WEIGHT) {
throw std::runtime_error(strprintf("block_reserved_weight (%zu) must be at least %u weight units",
*options.block_reserved_weight,
MINIMUM_BLOCK_RESERVED_WEIGHT));
}
// Ensure m_tip_block is set so consumers of BlockTemplate can rely on that.
std::optional<BlockRef> maybe_tip{waitTipChanged(uint256::ZERO, MillisecondsDouble::max())};
@@ -990,10 +997,14 @@ public:
// Also wait during the final catch-up moments after IBD.
if (!CooldownIfHeadersAhead(chainman(), notifications(), *maybe_tip, m_interrupt_mining)) return {};
}
BlockAssembler::Options assemble_options{options};
ApplyArgsManOptions(*Assert(m_node.args), assemble_options);
return std::make_unique<BlockTemplateImpl>(assemble_options, BlockAssembler{chainman().ActiveChainstate(), context()->mempool.get(), assemble_options}.CreateNewBlock(), m_node);
const BlockCreateOptions create_options{MergeMiningOptions(options, m_node.mining_args)};
return std::make_unique<BlockTemplateImpl>(create_options,
BlockAssembler{
chainman().ActiveChainstate(),
m_node.mempool.get(),
create_options,
}.CreateNewBlock(),
m_node);
}
void interrupt() override
@@ -1010,12 +1021,12 @@ public:
return state.IsValid();
}
NodeContext* context() override { return &m_node; }
const NodeContext* context() override { return &m_node; }
ChainstateManager& chainman() { return *Assert(m_node.chainman); }
KernelNotifications& notifications() { return *Assert(m_node.notifications); }
// Treat as if guarded by notifications().m_tip_block_mutex
bool m_interrupt_mining{false};
NodeContext& m_node;
const NodeContext& m_node;
};
class RpcImpl : public Rpc
@@ -1041,7 +1052,7 @@ public:
namespace interfaces {
std::unique_ptr<Node> MakeNode(node::NodeContext& context) { return std::make_unique<node::NodeImpl>(context); }
std::unique_ptr<Chain> MakeChain(node::NodeContext& context) { return std::make_unique<node::ChainImpl>(context); }
std::unique_ptr<Mining> MakeMining(node::NodeContext& context, bool wait_loaded)
std::unique_ptr<Mining> MakeMining(const node::NodeContext& context, bool wait_loaded)
{
if (wait_loaded) {
node::KernelNotifications& kernel_notifications(*Assert(context.notifications));

View File

@@ -7,29 +7,49 @@
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
#include <common/args.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/params.h>
#include <consensus/tx_verify.h>
#include <consensus/validation.h>
#include <deploymentstatus.h>
#include <node/context.h>
#include <interfaces/types.h>
#include <node/blockstorage.h>
#include <node/kernel_notifications.h>
#include <node/mining_args.h>
#include <node/mining_types.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <sync.h>
#include <tinyformat.h>
#include <txgraph.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/check.h>
#include <util/feefrac.h>
#include <util/log.h>
#include <util/moneystr.h>
#include <util/result.h>
#include <util/signalinterrupt.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <versionbits.h>
#include <algorithm>
#include <utility>
#include <compare>
#include <condition_variable>
#include <cstddef>
#include <functional>
#include <numeric>
#include <span>
#include <stdexcept>
#include <string>
#include <utility>
namespace node {
@@ -76,38 +96,21 @@ void RegenerateCommitments(CBlock& block, ChainstateManager& chainman)
block.hashMerkleRoot = BlockMerkleRoot(block);
}
static BlockAssembler::Options ClampOptions(BlockAssembler::Options options)
{
// Apply DEFAULT_BLOCK_RESERVED_WEIGHT when the caller left it unset.
options.block_reserved_weight = std::clamp<size_t>(options.block_reserved_weight.value_or(DEFAULT_BLOCK_RESERVED_WEIGHT), MINIMUM_BLOCK_RESERVED_WEIGHT, MAX_BLOCK_WEIGHT);
options.coinbase_output_max_additional_sigops = std::clamp<size_t>(options.coinbase_output_max_additional_sigops, 0, MAX_BLOCK_SIGOPS_COST);
// Limit weight to between block_reserved_weight and MAX_BLOCK_WEIGHT for sanity:
// block_reserved_weight can safely exceed -blockmaxweight, but the rest of the block template will be empty.
options.nBlockMaxWeight = std::clamp<size_t>(options.nBlockMaxWeight, *options.block_reserved_weight, MAX_BLOCK_WEIGHT);
return options;
}
BlockAssembler::BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options)
BlockAssembler::BlockAssembler(Chainstate& chainstate,
const CTxMemPool* mempool,
BlockCreateOptions options)
: chainparams{chainstate.m_chainman.GetParams()},
m_mempool{options.use_mempool ? mempool : nullptr},
m_chainstate{chainstate},
m_options{ClampOptions(options)}
m_options{[&] {
if (auto result{CheckMiningOptions(options, /*use_argnames=*/false)}; !result) {
throw std::runtime_error(util::ErrorString(result).original);
}
return FlattenMiningOptions(std::move(options));
}()}
{
}
void ApplyArgsManOptions(const ArgsManager& args, BlockAssembler::Options& options)
{
// Block resource limits
options.nBlockMaxWeight = args.GetIntArg("-blockmaxweight", options.nBlockMaxWeight);
if (const auto blockmintxfee{args.GetArg("-blockmintxfee")}) {
if (const auto parsed{ParseMoney(*blockmintxfee)}) options.blockMinFeeRate = CFeeRate{*parsed};
}
options.print_modified_fee = args.GetBoolArg("-printpriority", options.print_modified_fee);
if (!options.block_reserved_weight) {
options.block_reserved_weight = args.GetIntArg("-blockreservedweight");
}
}
void BlockAssembler::resetBlock()
{
// Reserve space for fixed-size block header, txs count, and coinbase tx.
@@ -241,7 +244,7 @@ std::unique_ptr<CBlockTemplate> BlockAssembler::CreateNewBlock()
bool BlockAssembler::TestChunkBlockLimits(FeePerWeight chunk_feerate, int64_t chunk_sigops_cost) const
{
if (nBlockWeight + chunk_feerate.size >= m_options.nBlockMaxWeight) {
if (nBlockWeight + chunk_feerate.size >= m_options.block_max_weight) {
return false;
}
if (nBlockSigOpsCost + chunk_sigops_cost >= MAX_BLOCK_SIGOPS_COST) {
@@ -272,7 +275,7 @@ void BlockAssembler::AddToBlock(const CTxMemPoolEntry& entry)
nBlockSigOpsCost += entry.GetSigOpCost();
nFees += entry.GetFee();
if (m_options.print_modified_fee) {
if (*m_options.print_modified_fee) {
LogInfo("fee rate %s txid %s\n",
CFeeRate(entry.GetModifiedFee(), entry.GetTxSize()).ToString(),
entry.GetTx().GetHash().ToString());
@@ -298,7 +301,7 @@ void BlockAssembler::addChunks()
while (selected_transactions.size() > 0) {
// Check to see if min fee rate is still respected.
if (ByRatio{chunk_feerate_vsize} < ByRatio{m_options.blockMinFeeRate.GetFeePerVSize()}) {
if (ByRatio{chunk_feerate_vsize} < ByRatio{m_options.block_min_fee_rate->GetFeePerVSize()}) {
// Everything else we might consider has a lower feerate
return;
}
@@ -315,7 +318,7 @@ void BlockAssembler::addChunks()
++nConsecutiveFailed;
if (nConsecutiveFailed > MAX_CONSECUTIVE_FAILURES && nBlockWeight +
BLOCK_FULL_ENOUGH_WEIGHT_DELTA > m_options.nBlockMaxWeight) {
BLOCK_FULL_ENOUGH_WEIGHT_DELTA > m_options.block_max_weight) {
// Give up if we're close to full and haven't succeeded in a while
return;
}
@@ -365,8 +368,8 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
KernelNotifications& kernel_notifications,
CTxMemPool* mempool,
const std::unique_ptr<CBlockTemplate>& block_template,
const BlockWaitOptions& options,
const BlockAssembler::Options& assemble_options,
const BlockWaitOptions& wait_options,
const BlockCreateOptions& create_options,
bool& interrupt_wait)
{
// Delay calculating the current template fees, just in case a new block
@@ -376,7 +379,7 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
// Alternate waiting for a new tip and checking if fees have risen.
// The latter check is expensive so we only run it once per second.
auto now{NodeClock::now()};
const auto deadline = now + options.timeout;
const auto deadline = now + wait_options.timeout;
const MillisecondsDouble tick{1000};
const bool allow_min_difficulty{chainman.GetParams().GetConsensus().fPowAllowMinDifficultyBlocks};
@@ -423,12 +426,12 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
*
* We'll also create a new template if the tip changed during this iteration.
*/
if (options.fee_threshold < MAX_MONEY || tip_changed) {
if (wait_options.fee_threshold < MAX_MONEY || tip_changed) {
auto new_tmpl{BlockAssembler{
chainman.ActiveChainstate(),
mempool,
assemble_options}
.CreateNewBlock()};
create_options
}.CreateNewBlock()};
// If the tip changed, return the new template regardless of its fees.
if (tip_changed) return new_tmpl;
@@ -440,8 +443,8 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
// Check if fees increased enough to return the new template
const CAmount new_fees = std::accumulate(new_tmpl->vTxFees.begin(), new_tmpl->vTxFees.end(), CAmount{0});
Assume(options.fee_threshold != MAX_MONEY);
if (new_fees >= current_fees + options.fee_threshold) return new_tmpl;
Assume(wait_options.fee_threshold != MAX_MONEY);
if (new_fees >= current_fees + wait_options.fee_threshold) return new_tmpl;
}
now = NodeClock::now();

View File

@@ -6,39 +6,38 @@
#ifndef BITCOIN_NODE_MINER_H
#define BITCOIN_NODE_MINER_H
#include <interfaces/types.h>
#include <node/types.h>
#include <policy/policy.h>
#include <consensus/amount.h>
#include <node/mining_types.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <threadsafety.h>
#include <txmempool.h>
#include <util/feefrac.h>
#include <util/time.h>
#include <cstdint>
#include <memory>
#include <optional>
#include <vector>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/indexed_by.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/tag.hpp>
#include <boost/multi_index_container.hpp>
class ArgsManager;
class CBlockIndex;
class CChainParams;
class CScript;
class Chainstate;
class ChainstateManager;
namespace Consensus { struct Params; };
namespace Consensus {
struct Params;
} // namespace Consensus
class uint256;
namespace interfaces {
struct BlockRef;
} // namespace interfaces
using interfaces::BlockRef;
namespace node {
class KernelNotifications;
static const bool DEFAULT_PRINT_MODIFIED_FEE = false;
struct CBlockTemplate
{
CBlock block;
@@ -78,16 +77,9 @@ private:
Chainstate& m_chainstate;
public:
struct Options : BlockCreateOptions {
// Configuration parameters for the block size
size_t nBlockMaxWeight{DEFAULT_BLOCK_MAX_WEIGHT};
CFeeRate blockMinFeeRate{DEFAULT_BLOCK_MIN_TX_FEE};
// Whether to call TestBlockValidity() at the end of CreateNewBlock().
bool test_block_validity{true};
bool print_modified_fee{DEFAULT_PRINT_MODIFIED_FEE};
};
explicit BlockAssembler(Chainstate& chainstate, const CTxMemPool* mempool, const Options& options);
explicit BlockAssembler(Chainstate& chainstate,
const CTxMemPool* mempool,
BlockCreateOptions create_options);
/** Construct a new block template */
std::unique_ptr<CBlockTemplate> CreateNewBlock();
@@ -98,7 +90,7 @@ public:
inline static std::optional<int64_t> m_last_block_weight{};
private:
const Options m_options;
const BlockCreateOptions m_options;
// utility functions
/** Clear the block's state and prepare for assembling a new block */
@@ -134,9 +126,6 @@ int64_t UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParam
/** Update an old GenerateCoinbaseCommitment from CreateNewBlock after the block txs have changed */
void RegenerateCommitments(CBlock& block, ChainstateManager& chainman);
/** Apply -blockmintxfee and -blockmaxweight options from ArgsManager to BlockAssembler options. */
void ApplyArgsManOptions(const ArgsManager& gArgs, BlockAssembler::Options& options);
/* Compute the block's merkle root, insert or replace the coinbase transaction and the merkle root into the block */
void AddMerkleRootAndCoinbase(CBlock& block, CTransactionRef coinbase, uint32_t version, uint32_t timestamp, uint32_t nonce);
@@ -151,8 +140,8 @@ std::unique_ptr<CBlockTemplate> WaitAndCreateNewBlock(ChainstateManager& chainma
KernelNotifications& kernel_notifications,
CTxMemPool* mempool,
const std::unique_ptr<CBlockTemplate>& block_template,
const BlockWaitOptions& options,
const BlockAssembler::Options& assemble_options,
const BlockWaitOptions& wait_options,
const BlockCreateOptions& create_options,
bool& interrupt_wait);
/* Locks cs_main and returns the block hash and block height of the active chain if it exists; otherwise, returns nullopt.*/

View File

@@ -5,13 +5,12 @@
#ifndef BITCOIN_NODE_MINI_MINER_H
#define BITCOIN_NODE_MINI_MINER_H
#include <attributes.h>
#include <consensus/amount.h>
#include <primitives/transaction.h>
#include <uint256.h>
#include <cstdint>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <vector>

99
src/node/mining_args.cpp Normal file
View File

@@ -0,0 +1,99 @@
// Copyright (c) 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 <node/mining_args.h>
#include <common/args.h>
#include <common/messages.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <node/mining_types.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <tinyformat.h>
#include <util/moneystr.h>
#include <util/result.h>
#include <util/translation.h>
#include <cstdint>
#include <optional>
#include <string>
#include <utility>
using common::AmountErrMsg;
using util::Error;
using util::Result;
namespace node {
Result<void> CheckMiningOptions(BlockCreateOptions options, bool use_argnames)
{
options = FlattenMiningOptions(std::move(options));
if (*options.block_reserved_weight < MINIMUM_BLOCK_RESERVED_WEIGHT) {
return Error{Untranslated(strprintf("%s (%d) is lower than minimum safety value of (%d)",
use_argnames ? "-blockreservedweight" : "block_reserved_weight",
*options.block_reserved_weight, MINIMUM_BLOCK_RESERVED_WEIGHT))};
}
if (*options.block_reserved_weight > MAX_BLOCK_WEIGHT) {
return Error{Untranslated(strprintf("%s (%d) exceeds consensus maximum block weight (%d)",
use_argnames ? "-blockreservedweight" : "block_reserved_weight",
*options.block_reserved_weight, MAX_BLOCK_WEIGHT))};
}
if (*options.block_max_weight > MAX_BLOCK_WEIGHT) {
return Error{Untranslated(strprintf("%s (%d) exceeds consensus maximum block weight (%d)",
use_argnames ? "-blockmaxweight" : "block_max_weight",
*options.block_max_weight, MAX_BLOCK_WEIGHT))};
}
if (*options.block_reserved_weight > *options.block_max_weight) {
return Error{Untranslated(strprintf("%s (%d) exceeds %s (%d)",
use_argnames ? "-blockreservedweight" : "block_reserved_weight",
*options.block_reserved_weight,
use_argnames ? "-blockmaxweight" : "block_max_weight",
*options.block_max_weight))};
}
if (options.coinbase_output_max_additional_sigops > MAX_BLOCK_SIGOPS_COST) {
return Error{Untranslated(strprintf("%s (%zu) exceeds consensus maximum block sigops cost (%d)",
"coinbase_output_max_additional_sigops",
options.coinbase_output_max_additional_sigops, MAX_BLOCK_SIGOPS_COST))};
}
return {};
}
Result<BlockCreateOptions> ReadMiningArgs(const ArgsManager& args)
{
BlockCreateOptions options;
if (const auto arg{args.GetArg("-blockmintxfee")}) {
std::optional<CAmount> block_min_tx_fee{ParseMoney(*arg)};
if (!block_min_tx_fee) return Error{AmountErrMsg("blockmintxfee", *arg)};
options.block_min_fee_rate = CFeeRate{*block_min_tx_fee};
}
if (const auto arg{args.GetBoolArg("-printpriority")}) options.print_modified_fee = *arg;
options.block_reserved_weight = args.GetArg<uint64_t>("-blockreservedweight");
options.block_max_weight = args.GetArg<uint64_t>("-blockmaxweight");
if (auto result{CheckMiningOptions(options, /*use_argnames=*/true)}; !result) return Error{util::ErrorString(result)};
return options;
}
BlockCreateOptions FlattenMiningOptions(BlockCreateOptions options)
{
if (!options.block_min_fee_rate) options.block_min_fee_rate = CFeeRate{DEFAULT_BLOCK_MIN_TX_FEE};
if (!options.print_modified_fee) options.print_modified_fee = DEFAULT_PRINT_MODIFIED_FEE;
if (!options.block_reserved_weight) options.block_reserved_weight = DEFAULT_BLOCK_RESERVED_WEIGHT;
if (!options.block_max_weight) options.block_max_weight = DEFAULT_BLOCK_MAX_WEIGHT;
return options;
}
BlockCreateOptions MergeMiningOptions(BlockCreateOptions x, const BlockCreateOptions& y)
{
if (!x.block_min_fee_rate) x.block_min_fee_rate = y.block_min_fee_rate;
if (!x.print_modified_fee) x.print_modified_fee = y.print_modified_fee;
if (!x.block_reserved_weight) x.block_reserved_weight = y.block_reserved_weight;
if (!x.block_max_weight) x.block_max_weight = y.block_max_weight;
return x;
}
} // namespace node

37
src/node/mining_args.h Normal file
View File

@@ -0,0 +1,37 @@
// Copyright (c) The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_NODE_MINING_ARGS_H
#define BITCOIN_NODE_MINING_ARGS_H
#include <node/mining_types.h>
#include <util/result.h>
class ArgsManager;
namespace node {
static const bool DEFAULT_PRINT_MODIFIED_FEE = false;
/**
* Read the mining options set in \p args. Returns an error if one was
* encountered.
*/
[[nodiscard]] util::Result<BlockCreateOptions> ReadMiningArgs(const ArgsManager& args);
/** Check option values for validity. Returns an error for invalid values. */
[[nodiscard]] util::Result<void> CheckMiningOptions(BlockCreateOptions options, bool use_argnames);
/** Replace null optional values with their hardcoded defaults. */
[[nodiscard]] BlockCreateOptions FlattenMiningOptions(BlockCreateOptions options);
/**
* Merge two BlockCreateOptions structs, replacing null values in \p x with
* non-null values from \p y.
*/
[[nodiscard]] BlockCreateOptions MergeMiningOptions(BlockCreateOptions x, const BlockCreateOptions& y);
} // namespace node
#endif // BITCOIN_NODE_MINING_ARGS_H

178
src/node/mining_types.h Normal file
View File

@@ -0,0 +1,178 @@
// Copyright (c) The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//! @file node/mining_types.h is used externally by mining IPC clients, so it should
//! only declare simple data definitions.
//!
//! Avoid declaring functions or classes with methods here unless they are
//! header-only or provided by the util library.
#ifndef BITCOIN_NODE_MINING_TYPES_H
#define BITCOIN_NODE_MINING_TYPES_H
#include <consensus/amount.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <uint256.h>
#include <util/time.h>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <vector>
namespace node {
/**
* Block template creation options. These override node defaults, but can't
* exceed node limits (e.g. block_reserved_weight can't exceed max block weight).
*/
struct BlockCreateOptions {
/**
* Set false to omit mempool transactions
*/
bool use_mempool{true};
/**
* Minimum fee rate for transactions to be included. Providing a value
* overrides the -blockmintxfee startup setting.
*/
std::optional<CFeeRate> block_min_fee_rate{};
/**
* Whether to log the fee rate of each transaction when it is added to the
* block template. Providing a value overrides the -printpriority startup
* setting.
*/
std::optional<bool> print_modified_fee{};
/**
* The default reserved weight for the fixed-size block header,
* transaction count and coinbase transaction. Minimum: 2000 weight units
* (MINIMUM_BLOCK_RESERVED_WEIGHT).
*
* Providing a value overrides the `-blockreservedweight` startup setting.
* Cap'n Proto IPC clients currently cannot leave this field unset, so they
* always provide a value.
*/
std::optional<uint64_t> block_reserved_weight{};
/**
* Maximum block weight, defaults to -maxblockweight
*
* Must not be lower than block_reserved_weight. Setting this equal to
* block_reserved_weight leaves no room for non-coinbase transactions.
*/
std::optional<uint64_t> block_max_weight{};
/**
* The maximum additional sigops which the pool will add in coinbase
* transaction outputs.
*/
size_t coinbase_output_max_additional_sigops{DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS};
/**
* Script to put in the coinbase transaction. The default is an
* anyone-can-spend dummy.
*
* Should only be used for tests, when the default doesn't suffice.
*
* Note that higher level code like the getblocktemplate RPC may omit the
* coinbase transaction entirely. It's instead constructed by pool software
* using fields like coinbasevalue, coinbaseaux and default_witness_commitment.
* This software typically also controls the payout outputs, even for solo
* mining.
*
* The size and sigops are not checked against
* coinbase_max_additional_weight and coinbase_output_max_additional_sigops.
*/
CScript coinbase_output_script{CScript() << OP_TRUE};
/**
* Whether to call TestBlockValidity() at the end of CreateNewBlock().
* Should only be used for tests / benchmarks.
*/
bool test_block_validity{true};
};
struct BlockWaitOptions {
/**
* How long to wait before returning nullptr instead of a new template.
* Default is to wait forever.
*/
MillisecondsDouble timeout{MillisecondsDouble::max()};
/**
* The wait method will not return a new template unless it has fees at
* least fee_threshold sats higher than the current template, or unless
* the chain tip changes and the previous template is no longer valid.
*
* A caller may not be interested in templates with higher fees, and
* determining whether fee_threshold is reached is also expensive. So as
* an optimization, when fee_threshold is set to MAX_MONEY (default), the
* implementation is able to be much more efficient, skipping expensive
* checks and only returning new templates when the chain tip changes.
*/
CAmount fee_threshold{MAX_MONEY};
};
struct BlockCheckOptions {
/**
* Set false to omit the merkle root check
*/
bool check_merkle_root{true};
/**
* Set false to omit the proof-of-work check
*/
bool check_pow{true};
};
/**
* Template containing all coinbase transaction fields that are set by our
* miner code. Clients are expected to add their own outputs and typically
* also expand the scriptSig.
*/
struct CoinbaseTx {
/* nVersion */
uint32_t version;
/* nSequence for the only coinbase transaction input */
uint32_t sequence;
/**
* Prefix which needs to be placed at the beginning of the scriptSig.
* Clients may append extra data to this as long as the overall scriptSig
* size is 100 bytes or less, to avoid the block being rejected with
* "bad-cb-length" error. At heights <= 16 the BIP 34 height push is only
* one byte long, so clients must append at least one additional byte to
* meet the consensus minimum scriptSig length of two bytes.
*
* Currently with BIP 34, the prefix is guaranteed to be less than 8 bytes,
* but future soft forks could require longer prefixes.
*/
CScript script_sig_prefix;
/**
* The first (and only) witness stack element of the coinbase input.
*
* Omitted for block templates without witness data.
*
* This is currently the BIP 141 witness reserved value, and can be chosen
* arbitrarily by the node, but future soft forks may constrain it.
*/
std::optional<uint256> witness;
/**
* Block subsidy plus fees, minus any non-zero required_outputs.
*
* Currently there are no non-zero required_outputs, so block_reward_remaining
* is the entire block reward. See also required_outputs.
*/
CAmount block_reward_remaining;
/*
* To be included as the last outputs in the coinbase transaction.
* Currently this is only the witness commitment OP_RETURN, but future
* softforks or a custom mining patch could add more.
*
* The dummy output that spends the full reward is excluded.
*/
std::vector<CTxOut> required_outputs;
uint32_t lock_time;
};
} // namespace node
#endif // BITCOIN_NODE_MINING_TYPES_H

View File

@@ -13,16 +13,7 @@
#ifndef BITCOIN_NODE_TYPES_H
#define BITCOIN_NODE_TYPES_H
#include <consensus/amount.h>
#include <cstddef>
#include <cstdint>
#include <optional>
#include <policy/policy.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <uint256.h>
#include <util/time.h>
#include <vector>
namespace node {
enum class TransactionError {
@@ -36,126 +27,6 @@ enum class TransactionError {
INVALID_PACKAGE,
};
struct BlockCreateOptions {
/**
* Set false to omit mempool transactions
*/
bool use_mempool{true};
/**
* The default reserved weight for the fixed-size block header,
* transaction count and coinbase transaction. Minimum: 2000 weight units
* (MINIMUM_BLOCK_RESERVED_WEIGHT).
*
* Providing a value overrides the `-blockreservedweight` startup setting.
* Cap'n Proto IPC clients currently cannot leave this field unset, so they
* always provide a value.
*/
std::optional<size_t> block_reserved_weight{};
/**
* The maximum additional sigops which the pool will add in coinbase
* transaction outputs.
*/
size_t coinbase_output_max_additional_sigops{DEFAULT_COINBASE_OUTPUT_MAX_ADDITIONAL_SIGOPS};
/**
* Script to put in the coinbase transaction. The default is an
* anyone-can-spend dummy.
*
* Should only be used for tests, when the default doesn't suffice.
*
* Note that higher level code like the getblocktemplate RPC may omit the
* coinbase transaction entirely. It's instead constructed by pool software
* using fields like coinbasevalue, coinbaseaux and default_witness_commitment.
* This software typically also controls the payout outputs, even for solo
* mining.
*
* The size and sigops are not checked against
* coinbase_max_additional_weight and coinbase_output_max_additional_sigops.
*/
CScript coinbase_output_script{CScript() << OP_TRUE};
};
struct BlockWaitOptions {
/**
* How long to wait before returning nullptr instead of a new template.
* Default is to wait forever.
*/
MillisecondsDouble timeout{MillisecondsDouble::max()};
/**
* The wait method will not return a new template unless it has fees at
* least fee_threshold sats higher than the current template, or unless
* the chain tip changes and the previous template is no longer valid.
*
* A caller may not be interested in templates with higher fees, and
* determining whether fee_threshold is reached is also expensive. So as
* an optimization, when fee_threshold is set to MAX_MONEY (default), the
* implementation is able to be much more efficient, skipping expensive
* checks and only returning new templates when the chain tip changes.
*/
CAmount fee_threshold{MAX_MONEY};
};
struct BlockCheckOptions {
/**
* Set false to omit the merkle root check
*/
bool check_merkle_root{true};
/**
* Set false to omit the proof-of-work check
*/
bool check_pow{true};
};
/**
* Template containing all coinbase transaction fields that are set by our
* miner code. Clients are expected to add their own outputs and typically
* also expand the scriptSig.
*/
struct CoinbaseTx {
/* nVersion */
uint32_t version;
/* nSequence for the only coinbase transaction input */
uint32_t sequence;
/**
* Prefix which needs to be placed at the beginning of the scriptSig.
* Clients may append extra data to this as long as the overall scriptSig
* size is 100 bytes or less, to avoid the block being rejected with
* "bad-cb-length" error. At heights <= 16 the BIP 34 height push is only
* one byte long, so clients must append at least one additional byte to
* meet the consensus minimum scriptSig length of two bytes.
*
* Currently with BIP 34, the prefix is guaranteed to be less than 8 bytes,
* but future soft forks could require longer prefixes.
*/
CScript script_sig_prefix;
/**
* The first (and only) witness stack element of the coinbase input.
*
* Omitted for block templates without witness data.
*
* This is currently the BIP 141 witness reserved value, and can be chosen
* arbitrarily by the node, but future soft forks may constrain it.
*/
std::optional<uint256> witness;
/**
* Block subsidy plus fees, minus any non-zero required_outputs.
*
* Currently there are no non-zero required_outputs, so block_reward_remaining
* is the entire block reward. See also required_outputs.
*/
CAmount block_reward_remaining;
/*
* To be included as the last outputs in the coinbase transaction.
* Currently this is only the witness commitment OP_RETURN, but future
* softforks or a custom mining patch could add more.
*
* The dummy output that spends the full reward is excluded.
*/
std::vector<CTxOut> required_outputs;
uint32_t lock_time;
};
/**
* How to broadcast a local transaction.
* Used to influence `BroadcastTransaction()` and its callers.

View File

@@ -5,46 +5,77 @@
#include <bitcoin-build-config.h> // IWYU pragma: keep
#include <interfaces/mining.h>
#include <addresstype.h>
#include <arith_uint256.h>
#include <chain.h>
#include <chainparams.h>
#include <chainparamsbase.h>
#include <common/system.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/params.h>
#include <consensus/validation.h>
#include <core_io.h>
#include <deploymentinfo.h>
#include <deploymentstatus.h>
#include <interfaces/mining.h>
#include <crypto/hex_base.h>
#include <interfaces/types.h>
#include <key_io.h>
#include <net.h>
#include <netbase.h>
#include <node/blockstorage.h>
#include <node/context.h>
#include <node/miner.h>
#include <node/mining_args.h>
#include <node/mining_types.h>
#include <node/warnings.h>
#include <policy/ephemeral_policy.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <rpc/blockchain.h>
#include <rpc/mining.h>
#include <rpc/protocol.h>
#include <rpc/request.h>
#include <rpc/server.h>
#include <rpc/server_util.h>
#include <rpc/util.h>
#include <script/descriptor.h>
#include <script/script.h>
#include <script/signingprovider.h>
#include <serialize.h>
#include <streams.h>
#include <sync.h>
#include <tinyformat.h>
#include <txmempool.h>
#include <uint256.h>
#include <univalue.h>
#include <util/chaintype.h>
#include <util/check.h>
#include <util/signalinterrupt.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbits.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <initializer_list>
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <span>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
using interfaces::BlockRef;
using interfaces::BlockTemplate;
@@ -463,7 +494,7 @@ static RPCMethod getmininginfo()
CBlockIndex& tip{*CHECK_NONFATAL(active_chain.Tip())};
UniValue obj(UniValue::VOBJ);
obj.pushKV("blocks", active_chain.Height());
obj.pushKV("blocks", active_chain.Height());
if (BlockAssembler::m_last_block_weight) obj.pushKV("currentblockweight", *BlockAssembler::m_last_block_weight);
if (BlockAssembler::m_last_block_num_txs) obj.pushKV("currentblocktx", *BlockAssembler::m_last_block_num_txs);
obj.pushKV("bits", strprintf("%08x", tip.nBits));
@@ -471,9 +502,8 @@ static RPCMethod getmininginfo()
obj.pushKV("target", GetTarget(tip, chainman.GetConsensus().powLimit).GetHex());
obj.pushKV("networkhashps", getnetworkhashps().HandleRequest(request));
obj.pushKV("pooledtx", mempool.size());
BlockAssembler::Options assembler_options;
ApplyArgsManOptions(*node.args, assembler_options);
obj.pushKV("blockmintxfee", ValueFromAmount(assembler_options.blockMinFeeRate.GetFeePerK()));
const auto mining_options{node::FlattenMiningOptions(node.mining_args)};
obj.pushKV("blockmintxfee", ValueFromAmount(CHECK_NONFATAL(mining_options.block_min_fee_rate)->GetFeePerK()));
obj.pushKV("chain", chainman.GetParams().GetChainTypeString());
UniValue next(UniValue::VOBJ);

View File

@@ -5,6 +5,8 @@
#ifndef BITCOIN_RPC_MINING_H
#define BITCOIN_RPC_MINING_H
#include <cstdint>
/** Default max iterations to try in RPC generatetodescriptor, generatetoaddress, and generateblock. */
static const uint64_t DEFAULT_MAX_TRIES{1000000};

View File

@@ -4,25 +4,45 @@
#include <addresstype.h>
#include <blockfilter.h>
#include <chainparams.h>
#include <chain.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <index/base.h>
#include <index/blockfilterindex.h>
#include <interfaces/chain.h>
#include <node/miner.h>
#include <interfaces/mining.h>
#include <key.h>
#include <node/blockstorage.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <sync.h>
#include <test/util/blockfilter.h>
#include <test/util/common.h>
#include <test/util/common.h> // IWYU pragma: keep
#include <test/util/setup_common.h>
#include <util/byte_units.h>
#include <tinyformat.h>
#include <uint256.h>
#include <util/check.h>
#include <util/fs.h>
#include <util/time.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
#include <future>
using node::BlockAssembler;
#include <compare>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <future>
#include <memory>
#include <span>
#include <string>
#include <thread>
#include <utility>
#include <vector>
using node::BlockManager;
using node::CBlockTemplate;
BOOST_AUTO_TEST_SUITE(blockfilter_index_tests)
@@ -69,10 +89,12 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev,
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey)
{
BlockAssembler::Options options;
options.coinbase_output_script = scriptPubKey;
std::unique_ptr<CBlockTemplate> pblocktemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get(), options}.CreateNewBlock();
CBlock& block = pblocktemplate->block;
auto mining{interfaces::MakeMining(m_node)};
auto block_template{mining->createNewBlock({
.coinbase_output_script = scriptPubKey,
}, /*cooldown=*/false)};
BOOST_REQUIRE(block_template);
CBlock block{block_template->getBlock()};
block.hashPrevBlock = prev->GetBlockHash();
block.nTime = prev->nTime + 1;

View File

@@ -2,18 +2,31 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
#include <consensus/validation.h>
#include <index/coinstatsindex.h>
#include <interfaces/chain.h>
#include <kernel/coinstats.h>
#include <kernel/types.h>
#include <key.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <test/util/validation.h>
#include <util/byte_units.h>
#include <util/check.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
#include <memory>
#include <optional>
#include <span>
#include <vector>
using kernel::ChainstateRole;
BOOST_AUTO_TEST_SUITE(coinstatsindex_tests)
@@ -83,7 +96,7 @@ BOOST_FIXTURE_TEST_CASE(coinstatsindex_unclean_shutdown, TestChain100Setup)
CBlockIndex* new_block_index = nullptr;
{
const CScript script_pub_key{CScript() << ToByteVector(coinbaseKey.GetPubKey()) << OP_CHECKSIG};
const CBlock block = this->CreateBlock({}, script_pub_key, chainstate);
const CBlock block = this->CreateBlock({}, script_pub_key);
new_block = std::make_shared<CBlock>(block);

View File

@@ -14,12 +14,13 @@
#include <net_processing.h>
#include <netmessagemaker.h>
#include <node/blockstorage.h>
#include <node/miner.h>
#include <node/mining_types.h>
#include <policy/truc_policy.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <protocol.h>
#include <script/script.h>
#include <serialize.h>
#include <sync.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
@@ -43,7 +44,6 @@
#include <validationinterface.h>
#include <boost/multi_index/detail/hash_index_iterator.hpp>
#include <boost/operators.hpp>
#include <cstddef>
#include <cstdint>
@@ -52,7 +52,6 @@
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <thread>
#include <utility>
#include <vector>
@@ -121,7 +120,7 @@ void ResetChainmanAndMempool(TestingSetup& setup)
setup.m_make_chainman();
setup.LoadVerifyActivateChainstate();
node::BlockAssembler::Options options;
node::BlockCreateOptions options;
options.coinbase_output_script = P2WSH_OP_TRUE;
g_mature_coinbase.clear();

View File

@@ -2,27 +2,35 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <node/mini_miner.h>
#include <consensus/amount.h>
#include <kernel/cs_main.h>
#include <policy/feerate.h>
#include <primitives/transaction.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/mempool.h>
#include <test/util/mining.h>
#include <test/util/script.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <test/util/time.h>
#include <test/util/txmempool.h>
#include <node/miner.h>
#include <node/mini_miner.h>
#include <node/types.h>
#include <primitives/transaction.h>
#include <random.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/check.h>
#include <util/time.h>
#include <util/translation.h>
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <deque>
#include <functional>
#include <map>
#include <optional>
#include <utility>
#include <vector>
namespace {

View File

@@ -2,26 +2,50 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <node/context.h>
#include <node/mempool_args.h>
#include <node/miner.h>
#include <node/mining_types.h> // IWYU pragma: keep
#include <policy/feerate.h>
#include <policy/packages.h>
#include <policy/policy.h>
#include <policy/settings.h>
#include <policy/truc_policy.h>
#include <primitives/transaction.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/mempool.h>
#include <test/util/mining.h>
#include <test/util/random.h>
#include <test/util/script.h>
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/check.h>
#include <util/rbf.h>
#include <util/hasher.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
using node::BlockAssembler;
#include <cstddef>
#include <cstdint>
#include <functional>
#include <iterator>
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <span>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
using node::NodeContext;
namespace {
@@ -44,11 +68,10 @@ void initialize_tx_pool()
g_setup = testing_setup.get();
SetMockTime(WITH_LOCK(g_setup->m_node.chainman->GetMutex(), return g_setup->m_node.chainman->ActiveTip()->Time()));
BlockAssembler::Options options;
options.coinbase_output_script = P2WSH_EMPTY;
for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
COutPoint prevout{MineBlock(g_setup->m_node, options)};
COutPoint prevout{MineBlock(g_setup->m_node, {
.coinbase_output_script = P2WSH_EMPTY,
})};
if (i < COINBASE_MATURITY) {
// Remember the txids to avoid expensive disk access later on
g_outpoints_coinbase_init_mature.push_back(prevout);

View File

@@ -2,14 +2,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
#include <banman.h>
#include <consensus/consensus.h>
#include <kernel/chainparams.h>
#include <net.h>
#include <net_processing.h>
#include <node/warnings.h>
#include <node/mining_types.h>
#include <primitives/block.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>
@@ -17,18 +19,25 @@
#include <test/fuzz/util/net.h>
#include <test/util/mining.h>
#include <test/util/net.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <test/util/time.h>
#include <test/util/validation.h>
#include <util/check.h>
#include <util/time.h>
#include <validation.h>
#include <validationinterface.h>
#include <algorithm>
#include <array>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
namespace {
@@ -42,7 +51,7 @@ void ResetChainman(TestingSetup& setup)
setup.m_make_chainman();
setup.LoadVerifyActivateChainstate();
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
node::BlockAssembler::Options options;
node::BlockCreateOptions options;
MineBlock(setup.m_node, options);
}
}

View File

@@ -2,13 +2,16 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addrman.h>
#include <banman.h>
#include <consensus/consensus.h>
#include <kernel/chainparams.h>
#include <net.h>
#include <net_processing.h>
#include <node/warnings.h>
#include <node/mining_types.h>
#include <primitives/block.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>
@@ -16,13 +19,18 @@
#include <test/fuzz/util/net.h>
#include <test/util/mining.h>
#include <test/util/net.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <test/util/time.h>
#include <test/util/validation.h>
#include <util/time.h>
#include <validation.h>
#include <validationinterface.h>
#include <functional>
#include <ios>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
@@ -36,7 +44,7 @@ void ResetChainman(TestingSetup& setup)
setup.m_node.chainman.reset();
setup.m_make_chainman();
setup.LoadVerifyActivateChainstate();
node::BlockAssembler::Options options;
node::BlockCreateOptions options;
for (int i = 0; i < 2 * COINBASE_MATURITY; i++) {
MineBlock(setup.m_node, options);
}

View File

@@ -2,26 +2,53 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <coins.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/validation.h>
#include <node/context.h>
#include <node/mempool_args.h>
#include <node/miner.h>
#include <node/mining_types.h>
#include <policy/feerate.h>
#include <policy/packages.h>
#include <policy/policy.h>
#include <policy/truc_policy.h>
#include <primitives/block.h>
#include <primitives/transaction.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/mempool.h>
#include <test/util/mining.h>
#include <test/util/random.h>
#include <test/util/script.h>
#include <test/util/setup_common.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
#include <util/check.h>
#include <util/rbf.h>
#include <util/string.h>
#include <util/time.h>
#include <util/translation.h>
#include <validation.h>
#include <validationinterface.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <iterator>
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <span>
#include <string>
#include <utility>
#include <vector>
using node::BlockAssembler;
using node::BlockCreateOptions;
using node::NodeContext;
using util::ToString;
@@ -46,11 +73,10 @@ void initialize_tx_pool()
g_setup = testing_setup.get();
SetMockTime(WITH_LOCK(g_setup->m_node.chainman->GetMutex(), return g_setup->m_node.chainman->ActiveTip()->Time()));
BlockAssembler::Options options;
options.coinbase_output_script = P2WSH_OP_TRUE;
for (int i = 0; i < 2 * COINBASE_MATURITY; ++i) {
COutPoint prevout{MineBlock(g_setup->m_node, options)};
COutPoint prevout{MineBlock(g_setup->m_node, {
.coinbase_output_script = P2WSH_OP_TRUE,
})};
// Remember the txids to avoid expensive disk access later on
auto& outpoints = i < COINBASE_MATURITY ?
g_outpoints_coinbase_init_mature :
@@ -94,9 +120,10 @@ void Finish(FuzzedDataProvider& fuzzed_data_provider, MockedTxPool& tx_pool, Cha
{
WITH_LOCK(::cs_main, tx_pool.check(chainstate.CoinsTip(), chainstate.m_chain.Height() + 1));
{
BlockAssembler::Options options;
options.nBlockMaxWeight = fuzzed_data_provider.ConsumeIntegralInRange(0U, MAX_BLOCK_WEIGHT);
options.blockMinFeeRate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)};
BlockCreateOptions options{
.block_min_fee_rate = CFeeRate{ConsumeMoney(fuzzed_data_provider, /*max=*/COIN)},
.block_max_weight = fuzzed_data_provider.ConsumeIntegralInRange<uint64_t>(DEFAULT_BLOCK_RESERVED_WEIGHT, MAX_BLOCK_WEIGHT),
};
auto assembler = BlockAssembler{chainstate, &tx_pool, options};
auto block_template = assembler.CreateNewBlock();
Assert(block_template->block.vtx.size() >= 1);

View File

@@ -3,23 +3,33 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <chainparams.h>
#include <consensus/consensus.h>
#include <consensus/amount.h>
#include <consensus/merkle.h>
#include <kernel/coinstats.h>
#include <node/miner.h>
#include <script/interpreter.h>
#include <streams.h>
#include <primitives/block.h>
#include <primitives/transaction.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/util/mining.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <test/util/time.h>
#include <util/chaintype.h>
#include <util/time.h>
#include <txdb.h>
#include <uint256.h>
#include <util/check.h>
#include <validation.h>
using node::BlockAssembler;
#include <cstddef>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <utility>
#include <vector>
FUZZ_TARGET(utxo_total_supply)
{
@@ -44,11 +54,11 @@ FUZZ_TARGET(utxo_total_supply)
LOCK(chainman.GetMutex());
return chainman.ActiveHeight();
};
BlockAssembler::Options options;
options.coinbase_output_script = CScript() << OP_FALSE;
const auto PrepareNextBlock = [&]() {
// Use OP_FALSE to avoid BIP30 check from hitting early
auto block = PrepareBlock(node, options);
auto block = PrepareBlock(node, {
.coinbase_output_script = CScript() << OP_FALSE,
});
// Replace OP_FALSE with OP_TRUE
{
CMutableTransaction tx{*block->vtx.back()};

View File

@@ -3,15 +3,28 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <addresstype.h>
#include <chain.h>
#include <coins.h>
#include <common/system.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/tx_verify.h>
#include <interfaces/mining.h>
#include <interfaces/types.h>
#include <kernel/chainparams.h>
#include <node/miner.h>
#include <node/mining_types.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <test/util/random.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
#include <script/script.h>
#include <serialize.h>
#include <sync.h>
#include <test/util/common.h>
#include <test/util/setup_common.h>
#include <test/util/transaction_utils.h>
#include <test/util/txmempool.h>
#include <txmempool.h>
@@ -23,20 +36,24 @@
#include <util/translation.h>
#include <validation.h>
#include <versionbits.h>
#include <pow.h>
#include <test/util/common.h>
#include <test/util/setup_common.h>
#include <memory>
#include <vector>
#include <boost/test/unit_test.hpp>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <memory>
#include <optional>
#include <span>
#include <stdexcept>
#include <string>
#include <vector>
using namespace util::hex_literals;
using interfaces::BlockTemplate;
using interfaces::Mining;
using node::BlockAssembler;
using node::BlockCreateOptions;
namespace miner_tests {
struct MinerTestingSetup : public TestingSetup {
@@ -115,8 +132,9 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
{
CTxMemPool& tx_mempool{MakeMempool()};
auto mining{MakeMining()};
BlockAssembler::Options options;
options.coinbase_output_script = scriptPubKey;
BlockCreateOptions options{
.coinbase_output_script = scriptPubKey,
};
LOCK(tx_mempool.cs);
BOOST_CHECK(tx_mempool.size() == 0);
@@ -175,7 +193,12 @@ void MinerTestingSetup::TestPackageSelection(const CScript& scriptPubKey, const
BOOST_CHECK(block.vtx[3]->GetHash() == hashMediumFeeTx);
// Test the inclusion of package feerates in the block template and ensure they are sequential.
const auto block_package_feerates = BlockAssembler{m_node.chainman->ActiveChainstate(), &tx_mempool, options}.CreateNewBlock()->m_package_feerates;
// Can't use the Mining interface because it needs access to m_package_feerates.
const auto block_package_feerates = BlockAssembler{
m_node.chainman->ActiveChainstate(),
&tx_mempool,
m_node.mining_args,
}.CreateNewBlock()->m_package_feerates;
BOOST_CHECK(block_package_feerates.size() == 2);
// parent_tx and high_fee_tx are added to the block as a package.
@@ -333,8 +356,9 @@ void MinerTestingSetup::TestBasicMining(const CScript& scriptPubKey, const std::
auto mining{MakeMining()};
BOOST_REQUIRE(mining);
BlockAssembler::Options options;
options.coinbase_output_script = scriptPubKey;
BlockCreateOptions options{
.coinbase_output_script = scriptPubKey,
};
{
CTxMemPool& tx_mempool{MakeMempool()};
@@ -659,8 +683,9 @@ void MinerTestingSetup::TestPrioritisedMining(const CScript& scriptPubKey, const
auto mining{MakeMining()};
BOOST_REQUIRE(mining);
BlockAssembler::Options options;
options.coinbase_output_script = scriptPubKey;
BlockCreateOptions options{
.coinbase_output_script = scriptPubKey,
};
CTxMemPool& tx_mempool{MakeMempool()};
LOCK(tx_mempool.cs);
@@ -748,12 +773,20 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity)
// Note that by default, these tests run with size accounting enabled.
CScript scriptPubKey = CScript() << "04678afdb0fe5548271967f1a67130b7105cd6a828e03909a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c384df7ba0b8d578a4c702b6bf11d5f"_hex << OP_CHECKSIG;
BlockAssembler::Options options;
options.coinbase_output_script = scriptPubKey;
BlockCreateOptions options{
.coinbase_output_script = scriptPubKey,
};
// Create and check a simple template
std::unique_ptr<BlockTemplate> block_template = mining->createNewBlock(options, /*cooldown=*/false);
BOOST_REQUIRE(block_template);
BlockCreateOptions invalid_options{options};
invalid_options.block_max_weight = DEFAULT_BLOCK_RESERVED_WEIGHT - 1;
BOOST_CHECK_EXCEPTION(mining->createNewBlock(invalid_options, /*cooldown=*/false),
std::runtime_error,
HasReason("block_reserved_weight (8000) exceeds block_max_weight (7999)"));
{
CBlock block{block_template->getBlock()};
{

View File

@@ -2,26 +2,39 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or https://www.opensource.org/licenses/mit-license.php.
#include <chain.h>
#include <chainparams.h>
#include <node/miner.h>
#include <consensus/params.h>
#include <interfaces/mining.h>
#include <net_processing.h>
#include <pow.h>
#include <primitives/block.h>
#include <protocol.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <util/check.h>
#include <util/time.h>
#include <validation.h>
#include <validationinterface.h>
#include <boost/test/unit_test.hpp>
#include <cstdint>
#include <memory>
BOOST_FIXTURE_TEST_SUITE(peerman_tests, RegTestingSetup)
/** Window, in blocks, for connecting to NODE_NETWORK_LIMITED peers */
static constexpr int64_t NODE_NETWORK_LIMITED_ALLOW_CONN_BLOCKS = 144;
static void mineBlock(const node::NodeContext& node, std::chrono::seconds block_time)
static void mineBlock(node::NodeContext& node, std::chrono::seconds block_time)
{
auto curr_time = GetTime<std::chrono::seconds>();
node::BlockAssembler::Options options;
SetMockTime(block_time); // update time so the block is created with it
CBlock block = node::BlockAssembler{node.chainman->ActiveChainstate(), nullptr, options}.CreateNewBlock()->block;
auto mining{interfaces::MakeMining(node)};
auto block_template{mining->createNewBlock({}, /*cooldown=*/false)};
BOOST_REQUIRE(block_template);
CBlock block{block_template->getBlock()};
while (!CheckProofOfWork(block.GetHash(), block.nBits, node.chainman->GetConsensus())) ++block.nNonce;
block.fChecked = true; // little speedup
SetMockTime(curr_time); // process block at current time

View File

@@ -2,10 +2,11 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <common/system.h>
#include <chain.h>
#include <interfaces/mining.h>
#include <node/miner.h>
#include <test/util/common.h>
#include <node/mining_types.h>
#include <primitives/block.h>
#include <sync.h>
#include <test/util/setup_common.h>
#include <test/util/time.h>
#include <util/time.h>
@@ -13,9 +14,10 @@
#include <boost/test/unit_test.hpp>
#include <memory>
using interfaces::BlockTemplate;
using interfaces::Mining;
using node::BlockAssembler;
using node::BlockWaitOptions;
namespace testnet4_miner_tests {
@@ -35,18 +37,18 @@ BOOST_AUTO_TEST_CASE(MiningInterface)
auto mining{MakeMining()};
BOOST_REQUIRE(mining);
BlockAssembler::Options options;
std::unique_ptr<BlockTemplate> block_template;
// Set node time a few minutes past the testnet4 genesis block
const auto template_time{3min + WITH_LOCK(cs_main, return m_node.chainman->ActiveChain().Tip()->Time())};
NodeClockContext clock_ctx{template_time};
block_template = mining->createNewBlock(options, /*cooldown=*/false);
block_template = mining->createNewBlock({}, /*cooldown=*/false);
BOOST_REQUIRE(block_template);
// The template should use the mocked system time
BOOST_REQUIRE_EQUAL(block_template->getBlockHeader().Time(), template_time);
BOOST_REQUIRE_EQUAL(TicksSinceEpoch<std::chrono::seconds>(block_template->getBlockHeader().Time()),
TicksSinceEpoch<std::chrono::seconds>(template_time));
const BlockWaitOptions wait_options{.timeout = MillisecondsDouble{0}, .fee_threshold = 1};

View File

@@ -4,33 +4,40 @@
#include <test/util/mining.h>
#include <addresstype.h>
#include <chain.h>
#include <chainparams.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <interfaces/mining.h>
#include <key_io.h>
#include <node/context.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <sync.h>
#include <test/util/script.h>
#include <uint256.h>
#include <util/check.h>
#include <validation.h>
#include <validationinterface.h>
#include <versionbits.h>
#include <algorithm>
#include <cstdint>
#include <memory>
#include <optional>
#include <utility>
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);
return MineBlock(node, assembler_options);
return MineBlock(node, {
.coinbase_output_script = GetScriptForDestination(dest),
});
}
std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const CChainParams& params)
@@ -68,7 +75,7 @@ std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const
return ret;
}
COutPoint MineBlock(const NodeContext& node, const node::BlockAssembler::Options& assembler_options)
COutPoint MineBlock(const NodeContext& node, const node::BlockCreateOptions& assembler_options)
{
auto block = PrepareBlock(node, assembler_options);
auto valid = MineBlock(node, block);
@@ -122,12 +129,11 @@ COutPoint ProcessBlock(const NodeContext& node, const std::shared_ptr<CBlock>& b
}
std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node,
const BlockAssembler::Options& assembler_options)
const node::BlockCreateOptions& assembler_options)
{
auto block = std::make_shared<CBlock>(
BlockAssembler{Assert(node.chainman)->ActiveChainstate(), Assert(node.mempool.get()), assembler_options}
.CreateNewBlock()
->block);
auto mining = interfaces::MakeMining(node);
auto block_template = mining->createNewBlock(assembler_options, /*cooldown=*/false);
auto block = std::make_shared<CBlock>(Assert(block_template)->getBlock());
LOCK(cs_main);
block->nTime = Assert(node.chainman)->ActiveChain().Tip()->GetMedianTimePast() + 1;
@@ -135,10 +141,3 @@ std::shared_ptr<CBlock> PrepareBlock(const NodeContext& node,
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;
ApplyArgsManOptions(*node.args, assembler_options);
return PrepareBlock(node, assembler_options);
}

View File

@@ -5,8 +5,7 @@
#ifndef BITCOIN_TEST_UTIL_MINING_H
#define BITCOIN_TEST_UTIL_MINING_H
#include <node/miner.h>
#include <cstddef>
#include <memory>
#include <string>
#include <vector>
@@ -14,8 +13,8 @@
class CBlock;
class CChainParams;
class COutPoint;
class CScript;
namespace node {
struct BlockCreateOptions;
struct NodeContext;
} // namespace node
@@ -24,7 +23,7 @@ std::vector<std::shared_ptr<CBlock>> CreateBlockChain(size_t total_height, const
/** Returns the generated coin */
COutPoint MineBlock(const node::NodeContext&,
const node::BlockAssembler::Options& assembler_options);
const node::BlockCreateOptions& assembler_options);
/**
* Returns the generated coin (or Null if the block was invalid).
@@ -38,9 +37,8 @@ COutPoint MineBlock(const node::NodeContext&, std::shared_ptr<CBlock>& block);
COutPoint ProcessBlock(const node::NodeContext&, const std::shared_ptr<CBlock>& block);
/** Prepare a block to be mined */
std::shared_ptr<CBlock> PrepareBlock(const node::NodeContext&);
std::shared_ptr<CBlock> PrepareBlock(const node::NodeContext& node,
const node::BlockAssembler::Options& assembler_options);
const node::BlockCreateOptions& assembler_options);
/** RPC-like helper function, returns the generated coin */
COutPoint generatetoaddress(const node::NodeContext&, const std::string& address);

View File

@@ -6,50 +6,68 @@
#include <addrman.h>
#include <banman.h>
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
#include <common/system.h>
#include <consensus/amount.h>
#include <consensus/consensus.h>
#include <consensus/params.h>
#include <consensus/validation.h>
#include <crypto/sha256.h>
#include <crypto/hex_base.h>
#include <dbwrapper.h>
#include <init.h>
#include <init/common.h>
#include <interfaces/chain.h>
#include <kernel/mempool_entry.h>
#include <interfaces/mining.h>
#include <kernel/caches.h>
#include <kernel/context.h>
#include <key.h>
#include <logging.h>
#include <net.h>
#include <net_processing.h>
#include <netbase.h>
#include <netgroup.h>
#include <node/blockstorage.h>
#include <node/chainstate.h>
#include <node/context.h>
#include <node/kernel_notifications.h>
#include <node/mempool_args.h>
#include <node/miner.h>
#include <node/mining_args.h>
#include <node/mining_types.h>
#include <node/peerman_args.h>
#include <node/warnings.h>
#include <noui.h>
#include <policy/fees/block_policy_estimator.h>
#include <policy/feerate.h>
#include <policy/policy.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
#include <rpc/blockchain.h>
#include <rpc/register.h>
#include <rpc/server.h>
#include <scheduler.h>
#include <script/sigcache.h>
#include <script/interpreter.h>
#include <script/script.h>
#include <script/sign.h>
#include <script/signingprovider.h>
#include <serialize.h>
#include <span.h>
#include <streams.h>
#include <sync.h>
#include <test/util/coverage.h>
#include <test/util/net.h>
#include <test/util/random.h>
#include <test/util/transaction_utils.h>
#include <test/util/txmempool.h>
#include <txdb.h>
#include <tinyformat.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/chaintype.h>
#include <util/check.h>
#include <util/fs.h>
#include <util/fs_helpers.h>
#include <util/rbf.h>
#include <util/result.h>
#include <util/signalinterrupt.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/task_runner.h>
#include <util/thread.h>
#include <util/threadnames.h>
@@ -58,16 +76,27 @@
#include <util/vector.h>
#include <validation.h>
#include <validationinterface.h>
#include <walletinitinterface.h>
#include <algorithm>
#include <future>
#include <array>
#include <atomic>
#include <cstdlib>
#include <deque>
#include <functional>
#include <future>
#include <iostream>
#include <iterator>
#include <map>
#include <numeric>
#include <span>
#include <stdexcept>
#include <string_view>
#include <thread>
#include <tuple>
#include <utility>
using namespace util::hex_literals;
using node::ApplyArgsManOptions;
using node::BlockAssembler;
using node::BlockManager;
using node::KernelNotifications;
using node::LoadChainstate;
@@ -335,6 +364,8 @@ void ChainTestingSetup::LoadVerifyActivateChainstate()
std::tie(status, error) = VerifyLoadedChainstate(chainman, options);
assert(status == node::ChainstateLoadStatus::SUCCESS);
m_node.notifications->setChainstateLoaded(true);
BlockValidationState state;
if (!chainman.ActiveChainstate().ActivateBestChain(state)) {
throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString()));
@@ -362,6 +393,9 @@ TestingSetup::TestingSetup(
m_node.args->GetIntArg("-checkaddrman", 0));
m_node.banman = std::make_unique<BanMan>(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME);
m_node.connman = std::make_unique<ConnmanTestMsg>(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman, Params()); // Deterministic randomness for tests.
auto mining_args{node::ReadMiningArgs(*m_node.args)};
Assert(mining_args);
m_node.mining_args = std::move(*mining_args);
PeerManager::Options peerman_opts;
ApplyArgsManOptions(*m_node.args, peerman_opts);
peerman_opts.deterministic_rng = true;
@@ -411,12 +445,15 @@ void TestChain100Setup::mineBlocks(int num_blocks)
CBlock TestChain100Setup::CreateBlock(
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey,
Chainstate& chainstate)
const CScript& scriptPubKey)
{
BlockAssembler::Options options;
options.coinbase_output_script = scriptPubKey;
CBlock block = BlockAssembler{chainstate, nullptr, options}.CreateNewBlock()->block;
auto mining{interfaces::MakeMining(m_node)};
auto block_template{mining->createNewBlock({
.use_mempool = false,
.coinbase_output_script = scriptPubKey,
}, /*cooldown=*/false)};
Assert(block_template);
CBlock block{block_template->getBlock()};
Assert(block.vtx.size() == 1);
for (const CMutableTransaction& tx : txns) {
@@ -431,14 +468,9 @@ CBlock TestChain100Setup::CreateBlock(
CBlock TestChain100Setup::CreateAndProcessBlock(
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey,
Chainstate* chainstate)
const CScript& scriptPubKey)
{
if (!chainstate) {
chainstate = &Assert(m_node.chainman)->ActiveChainstate();
}
CBlock block = this->CreateBlock(txns, scriptPubKey, *chainstate);
CBlock block = this->CreateBlock(txns, scriptPubKey);
std::shared_ptr<const CBlock> shared_pblock = std::make_shared<const CBlock>(block);
Assert(m_node.chainman)->ProcessNewBlock(shared_pblock, true, true, nullptr);

View File

@@ -6,34 +6,29 @@
#define BITCOIN_TEST_UTIL_SETUP_COMMON_H
#include <common/args.h> // IWYU pragma: export
#include <consensus/amount.h>
#include <kernel/caches.h>
#include <kernel/context.h>
#include <key.h>
#include <node/caches.h>
#include <node/context.h> // IWYU pragma: export
#include <optional>
#include <ostream>
#include <primitives/transaction.h>
#include <pubkey.h>
#include <stdexcept>
#include <random.h>
#include <test/util/random.h>
#include <util/chaintype.h> // IWYU pragma: export
#include <util/check.h>
#include <util/fs.h>
#include <util/signalinterrupt.h>
#include <util/string.h>
#include <util/vector.h>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <type_traits>
#include <memory>
#include <optional>
#include <string>
#include <utility>
#include <vector>
class arith_uint256;
class CFeeRate;
class Chainstate;
class FastRandomContext;
class uint160;
class uint256;
/** Retrieve the command line arguments. */
extern const std::function<std::vector<const char*>()> G_TEST_COMMAND_LINE_ARGUMENTS;
@@ -134,7 +129,6 @@ struct Testnet4Setup : public TestingSetup {
};
class CBlock;
struct CMutableTransaction;
class CScript;
/**
@@ -148,11 +142,9 @@ struct TestChain100Setup : public TestingSetup {
/**
* Create a new block with just given transactions, coinbase paying to
* scriptPubKey, and try to add it to the current chain.
* If no chainstate is specified, default to the active.
*/
CBlock CreateAndProcessBlock(const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey,
Chainstate* chainstate = nullptr);
const CScript& scriptPubKey);
/**
* Create a new block with just given transactions, coinbase paying to
@@ -160,8 +152,7 @@ struct TestChain100Setup : public TestingSetup {
*/
CBlock CreateBlock(
const std::vector<CMutableTransaction>& txns,
const CScript& scriptPubKey,
Chainstate& chainstate);
const CScript& scriptPubKey);
//! Mine a series of new blocks on the active chain.
void mineBlocks(int num_blocks);

View File

@@ -2,26 +2,39 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <boost/test/unit_test.hpp>
#include <chain.h>
#include <chainparams.h>
#include <consensus/consensus.h>
#include <consensus/merkle.h>
#include <consensus/validation.h>
#include <node/miner.h>
#include <interfaces/mining.h>
#include <node/blockstorage.h>
#include <pow.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
#include <test/util/common.h>
#include <test/util/random.h>
#include <script/script.h>
#include <sync.h>
#include <test/util/common.h> // IWYU pragma: keep
#include <test/util/script.h>
#include <test/util/setup_common.h>
#include <util/time.h>
#include <txmempool.h>
#include <uint256.h>
#include <util/check.h>
#include <validation.h>
#include <validationinterface.h>
#include <boost/test/unit_test.hpp>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <span>
#include <thread>
#include <utility>
#include <vector>
using kernel::ChainstateRole;
using node::BlockAssembler;
namespace validation_block_tests {
struct MinerTestingSetup : public RegTestingSetup {
@@ -67,10 +80,12 @@ std::shared_ptr<CBlock> MinerTestingSetup::Block(const uint256& prev_hash)
static int i = 0;
static uint64_t time = Params().GenesisBlock().nTime;
BlockAssembler::Options options;
options.coinbase_output_script = CScript{} << i++ << OP_TRUE;
auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get(), options}.CreateNewBlock();
auto pblock = std::make_shared<CBlock>(ptemplate->block);
auto mining{interfaces::MakeMining(m_node)};
auto block_template{mining->createNewBlock({
.coinbase_output_script = CScript{} << i++ << OP_TRUE,
}, /*cooldown=*/false)};
BOOST_REQUIRE(block_template);
auto pblock = std::make_shared<CBlock>(block_template->getBlock());
pblock->hashPrevBlock = prev_hash;
pblock->nTime = ++time;
@@ -335,10 +350,12 @@ BOOST_AUTO_TEST_CASE(witness_commitment_index)
LOCK(Assert(m_node.chainman)->GetMutex());
CScript pubKey;
pubKey << 1 << OP_TRUE;
BlockAssembler::Options options;
options.coinbase_output_script = pubKey;
auto ptemplate = BlockAssembler{m_node.chainman->ActiveChainstate(), m_node.mempool.get(), options}.CreateNewBlock();
CBlock pblock = ptemplate->block;
auto mining{interfaces::MakeMining(m_node)};
auto block_template{mining->createNewBlock({
.coinbase_output_script = pubKey,
}, /*cooldown=*/false)};
BOOST_REQUIRE(block_template);
CBlock pblock{block_template->getBlock()};
CTxOut witness;
witness.scriptPubKey.resize(MINIMUM_WITNESS_COMMITMENT);

View File

@@ -2,27 +2,34 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
//
#include <chain.h>
#include <chainparams.h>
#include <coins.h>
#include <consensus/amount.h>
#include <consensus/validation.h>
#include <node/blockstorage.h>
#include <node/kernel_notifications.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <random.h>
#include <rpc/blockchain.h>
#include <script/script.h>
#include <sync.h>
#include <test/util/chainstate.h>
#include <test/util/common.h>
#include <test/util/common.h> // IWYU pragma: keep
#include <test/util/coins.h>
#include <test/util/random.h>
#include <test/util/setup_common.h>
#include <tinyformat.h>
#include <uint256.h>
#include <util/byte_units.h>
#include <util/check.h>
#include <validation.h>
#include <boost/test/unit_test.hpp>
#include <memory>
#include <optional>
#include <vector>
#include <boost/test/unit_test.hpp>
class CTxMemPool;
BOOST_FIXTURE_TEST_SUITE(validation_chainstate_tests, ChainTestingSetup)
@@ -83,7 +90,7 @@ BOOST_FIXTURE_TEST_CASE(connect_tip_does_not_cache_inputs_on_failed_connect, Tes
tx.vout.emplace_back(MAX_MONEY, CScript{} << OP_TRUE);
const auto tip{WITH_LOCK(cs_main, return chainstate.m_chain.Tip()->GetBlockHash())};
const CBlock block{CreateBlock({tx}, CScript{} << OP_TRUE, chainstate)};
const CBlock block{CreateBlock({tx}, CScript{} << OP_TRUE)};
BOOST_CHECK(Assert(m_node.chainman)->ProcessNewBlock(std::make_shared<CBlock>(block), true, true, nullptr));
LOCK(cs_main);

View File

@@ -6,38 +6,42 @@
import asyncio
import time
from contextlib import AsyncExitStack
from decimal import Decimal
from io import BytesIO
from test_framework.blocktools import NULL_OUTPOINT, script_BIP34_coinbase_height
from test_framework.messages import (
MAX_BLOCK_WEIGHT,
CBlockHeader,
COIN,
CTransaction,
CTxIn,
CTxOut,
CTxInWitness,
ser_uint256,
COIN,
CTxOut,
DEFAULT_BLOCK_RESERVED_WEIGHT,
MAX_BLOCK_SIGOPS_COST,
MAX_BLOCK_WEIGHT,
from_hex,
msg_headers,
ser_uint256,
)
from test_framework.test_framework import BitcoinTestFramework
from test_framework.util import (
assert_equal,
assert_greater_than_or_equal,
assert_not_equal
assert_not_equal,
)
from test_framework.wallet import MiniWallet
from test_framework.p2p import P2PInterface
from test_framework.ipc_util import (
assert_capnp_failed,
assert_create_new_block_fails,
destroying,
mining_create_block_template,
load_capnp_modules,
make_mining_ctx,
mining_create_block_template,
mining_get_block,
mining_get_coinbase_tx,
mining_wait_next_template,
wait_and_do,
make_mining_ctx,
assert_capnp_failed
)
# Test may be skipped and not have capnp installed
@@ -291,8 +295,9 @@ class IPCMiningTest(BitcoinTestFramework):
def run_ipc_option_override_test(self):
self.log.info("Running IPC option override test")
# Set an absurd reserved weight. `-blockreservedweight` is RPC-only, so
# with this setting RPC templates would be empty. IPC clients set
# Confirm that BlockCreateOptions.blockReservedWeight takes precedence
# over -blockreservedweight. Set an absurdly high -blockreservedweight
# value that would result in empty blocks to verify this. IPC clients set
# blockReservedWeight per template request and are unaffected; later in
# the test the IPC template includes a mempool transaction.
self.restart_node(0, extra_args=[f"-blockreservedweight={MAX_BLOCK_WEIGHT}"])
@@ -317,11 +322,136 @@ class IPCMiningTest(BitcoinTestFramework):
self.log.debug("Enforce minimum reserved weight for IPC clients too")
opts.blockReservedWeight = 0
try:
await mining.createNewBlock(ctx, opts)
raise AssertionError("createNewBlock unexpectedly succeeded")
except capnp.lib.capnp.KjException as e:
assert_capnp_failed(e, "remote exception: std::exception: block_reserved_weight (0) must be at least 2000 weight units")
await assert_create_new_block_fails(ctx, mining, opts,
"block_reserved_weight (0) is lower than minimum safety value of (2000)")
async def async_routine_check_max_reserved_weight():
self.log.debug("Enforce maximum reserved weight for IPC clients too")
ctx, mining = await make_mining_ctx(self)
opts = self.capnp_modules['mining'].BlockCreateOptions()
opts.blockReservedWeight = MAX_BLOCK_WEIGHT + 1
await assert_create_new_block_fails(ctx, mining, opts,
f"block_reserved_weight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})")
async def async_routine_check_sigops_limit():
self.log.debug("Enforce sigops limit for IPC clients too")
ctx, mining = await make_mining_ctx(self)
opts = self.capnp_modules['mining'].BlockCreateOptions()
opts.coinbaseOutputMaxAdditionalSigops = MAX_BLOCK_SIGOPS_COST + 1
await assert_create_new_block_fails(ctx, mining, opts,
f"coinbase_output_max_additional_sigops ({MAX_BLOCK_SIGOPS_COST + 1}) exceeds consensus maximum block sigops cost ({MAX_BLOCK_SIGOPS_COST})")
asyncio.run(capnp.run(async_routine()))
asyncio.run(capnp.run(async_routine_check_max_reserved_weight()))
asyncio.run(capnp.run(async_routine_check_sigops_limit()))
def run_waitnext_mining_policy_test(self):
"""Verify that waitNext() preserves the mining policy from -blockmintxfee
instead of falling back to defaults."""
self.log.info("Running waitNext mining policy test")
block_min_tx_fee = Decimal("0.00002000")
below_block_min_tx_fee = Decimal("0.00001000")
above_block_min_tx_fee = Decimal("0.00003000")
self.restart_node(0, extra_args=[
f"-blockmintxfee={block_min_tx_fee:.8f}",
"-minrelaytxfee=0",
"-persistmempool=0",
])
async def async_routine():
ctx, mining = await make_mining_ctx(self)
self.log.debug("Create a below -blockmintxfee transaction")
low_fee_tx = self.miniwallet.send_self_transfer(
fee_rate=below_block_min_tx_fee,
from_node=self.nodes[0],
confirmed_only=True,
)
assert low_fee_tx["txid"] in self.nodes[0].getrawmempool()
async with AsyncExitStack() as stack:
self.log.debug("createNewBlock should respect -blockmintxfee")
template = await mining_create_block_template(mining, stack, ctx, self.default_block_create_options)
assert template is not None
block = await mining_get_block(template, ctx)
assert low_fee_tx["txid"] not in {tx.txid_hex for tx in block.vtx[1:]}
self.log.debug("waitNext should preserve the same mining policy")
high_fee_tx = self.miniwallet.send_self_transfer(
fee_rate=above_block_min_tx_fee,
from_node=self.nodes[0],
confirmed_only=True,
)
mempool_txids = self.nodes[0].getrawmempool()
assert high_fee_tx["txid"] in mempool_txids
assert low_fee_tx["txid"] in mempool_txids
template_next = await mining_wait_next_template(template, stack, ctx, self.default_block_wait_options)
assert template_next is not None
block_next = await mining_get_block(template_next, ctx)
block_next_txids = {tx.txid_hex for tx in block_next.vtx[1:]}
assert high_fee_tx["txid"] in block_next_txids
assert low_fee_tx["txid"] not in block_next_txids
asyncio.run(capnp.run(async_routine()))
def run_block_max_weight_test(self):
"""Verify IPC createNewBlock() and waitNext() preserve the -blockmaxweight policy."""
self.log.info("Running block_max_weight test")
# Cap that leaves room for only a handful of mempool transactions
# above DEFAULT_BLOCK_RESERVED_WEIGHT (8000). Well below MAX_BLOCK_WEIGHT
# (4_000_000), so any truncation observed here is attributable to the
# cap, not to consensus limits or wallet chain limits.
small_cap = DEFAULT_BLOCK_RESERVED_WEIGHT + 4000
NUM_TXS = 20
self.restart_node(0, extra_args=[
f"-blockmaxweight={small_cap}",
"-minrelaytxfee=0",
"-persistmempool=0",
])
# Refresh miniwallet's UTXO view from the chain after restart.
self.miniwallet.rescan_utxos()
# Fill the mempool enough that the configured block weight cap forces
# template truncation.
for _ in range(NUM_TXS):
self.miniwallet.send_self_transfer(from_node=self.nodes[0], confirmed_only=True)
assert_equal(self.nodes[0].getmempoolinfo()["size"], NUM_TXS)
async def async_routine():
ctx, mining = await make_mining_ctx(self)
async with AsyncExitStack() as stack:
template = await mining_create_block_template(mining, stack, ctx, self.default_block_create_options)
assert template is not None
block = await mining_get_block(template, ctx)
assert_greater_than_or_equal(small_cap, block.get_weight())
# Exclude the coinbase; the cap must have forced truncation.
initial_included = len(block.vtx) - 1
assert initial_included < NUM_TXS, (
f"Expected -blockmaxweight={small_cap} to truncate; "
f"included {initial_included}/{NUM_TXS} mempool txs"
)
self.log.debug("waitNext should preserve -blockmaxweight")
high_fee_tx = self.miniwallet.send_self_transfer(
from_node=self.nodes[0],
confirmed_only=True,
fee_rate=10,
)
template_next = await mining_wait_next_template(template, stack, ctx, self.default_block_wait_options)
assert template_next is not None
block_next = await mining_get_block(template_next, ctx)
assert_greater_than_or_equal(small_cap, block_next.get_weight())
assert high_fee_tx["txid"] in {tx.txid_hex for tx in block_next.vtx[1:]}
next_included = len(block_next.vtx) - 1
assert next_included < NUM_TXS + 1, (
f"Expected -blockmaxweight={small_cap} to remain capped after waitNext; "
f"included {next_included}/{NUM_TXS + 1} mempool txs"
)
asyncio.run(capnp.run(async_routine()))
@@ -421,8 +551,6 @@ class IPCMiningTest(BitcoinTestFramework):
node.wait_for_rpc_connection()
assert_equal(node.getblockcount(), 0)
miniwallet = MiniWallet(node)
async def async_routine():
ctx, mining = await make_mining_ctx(self)
opts = self.capnp_modules['mining'].BlockCreateOptions()
@@ -437,7 +565,7 @@ class IPCMiningTest(BitcoinTestFramework):
block = await mining_get_block(template, ctx)
# Heights <= 16 need extra nonce padding.
extra_nonce = b'\xaa\xbb\xcc\xdd' if height <= 16 else b""
coinbase = await self.build_coinbase_test(template, ctx, miniwallet, extra_nonce=extra_nonce)
coinbase = await self.build_coinbase_test(template, ctx, self.miniwallet, extra_nonce=extra_nonce)
block.vtx[0] = coinbase
block.hashMerkleRoot = block.calc_merkle_root()
block.solve()
@@ -450,10 +578,15 @@ class IPCMiningTest(BitcoinTestFramework):
def run_test(self):
self.miniwallet = MiniWallet(self.nodes[0])
self.default_block_create_options = self.capnp_modules['mining'].BlockCreateOptions()
self.default_block_wait_options = self.capnp_modules['mining'].BlockWaitOptions()
self.default_block_wait_options.timeout = 1000.0 * self.options.timeout_factor
self.default_block_wait_options.feeThreshold = 1
self.run_mining_interface_test()
self.run_early_startup_test()
self.run_block_template_test()
self.run_coinbase_and_submission_test()
self.run_waitnext_mining_policy_test()
self.run_block_max_weight_test()
self.run_ipc_option_override_test()
# Needs to run last because it resets the chain.

View File

@@ -357,21 +357,28 @@ class MiningTest(BitcoinTestFramework):
self.stop_node(0)
self.nodes[0].assert_start_raises_init_error(
extra_args=[f"-blockreservedweight={MAX_BLOCK_WEIGHT + 1}"],
expected_msg=f"Error: Specified -blockreservedweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
expected_msg=f"Error: -blockreservedweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
)
self.log.info(f"Test that node will fail to start when user provide -blockreservedweight below {MINIMUM_BLOCK_RESERVED_WEIGHT}")
self.stop_node(0)
self.nodes[0].assert_start_raises_init_error(
extra_args=[f"-blockreservedweight={MINIMUM_BLOCK_RESERVED_WEIGHT - 1}"],
expected_msg=f"Error: Specified -blockreservedweight ({MINIMUM_BLOCK_RESERVED_WEIGHT - 1}) is lower than minimum safety value of ({MINIMUM_BLOCK_RESERVED_WEIGHT})",
expected_msg=f"Error: -blockreservedweight ({MINIMUM_BLOCK_RESERVED_WEIGHT - 1}) is lower than minimum safety value of ({MINIMUM_BLOCK_RESERVED_WEIGHT})",
)
self.log.info("Test that node will fail to start when user provide invalid -blockmaxweight")
self.stop_node(0)
self.nodes[0].assert_start_raises_init_error(
extra_args=[f"-blockmaxweight={MAX_BLOCK_WEIGHT + 1}"],
expected_msg=f"Error: Specified -blockmaxweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
expected_msg=f"Error: -blockmaxweight ({MAX_BLOCK_WEIGHT + 1}) exceeds consensus maximum block weight ({MAX_BLOCK_WEIGHT})",
)
self.log.info("Test that node will fail to start when -blockmaxweight is lower than -blockreservedweight")
self.stop_node(0)
self.nodes[0].assert_start_raises_init_error(
extra_args=[f"-blockmaxweight={DEFAULT_BLOCK_RESERVED_WEIGHT - 1}"],
expected_msg=f"Error: -blockreservedweight ({DEFAULT_BLOCK_RESERVED_WEIGHT}) exceeds -blockmaxweight ({DEFAULT_BLOCK_RESERVED_WEIGHT - 1})",
)
def test_height_in_locktime(self):

View File

@@ -162,3 +162,12 @@ async def make_mining_ctx(self):
def assert_capnp_failed(e, description_prefix):
assert e.description.startswith(description_prefix), f"Expected description starting with '{description_prefix}', got '{e.description}'"
assert_equal(e.type, "FAILED")
async def assert_create_new_block_fails(ctx, mining, opts, expected_msg):
"""Assert that mining.createNewBlock fails with the expected remote exception."""
try:
await mining.createNewBlock(ctx, opts)
raise AssertionError("createNewBlock unexpectedly succeeded")
except capnp.lib.capnp.KjException as e:
assert_capnp_failed(e, f"remote exception: std::exception: {expected_msg}")

View File

@@ -36,6 +36,7 @@ from test_framework.util import (
MAX_LOCATOR_SZ = 101
MAX_BLOCK_WEIGHT = 4000000
MAX_BLOCK_SIGOPS_COST = 80000
DEFAULT_BLOCK_RESERVED_WEIGHT = 8000
MINIMUM_BLOCK_RESERVED_WEIGHT = 2000
MAX_BLOOM_FILTER_SIZE = 36000