Repository: bitcoin/bitcoin
File: src/node/interfaces.cpp (C++)

Code before:
    bool isTestChain() override
    {
        return chainman().GetParams().IsTestChain();
    }

    bool isInitialBlockDownload() override
    {
        return chainman().IsInitialBlockDownload();
    }

    std::optional<BlockRef> getTip() override
    {
        return GetTip(chainman());
    }

    std::optional<BlockRef> waitTipChanged(uint256 current_tip, MillisecondsDouble timeout) override
    {
        return WaitTipChanged(chainman(), notifications(), current_tip, timeout);
    }

    std::unique_ptr<BlockTemplate> createNewBlock(const BlockCreateOptions& options) override
    {
        // Enforce minimum block_reserved_weight to avoid silently clamping it
        if (options.block_reserved_weight && options.block_reserved_weight < MINIMUM_BLOCK_RESERVED_WEIGHT) {
            return {};
        }

        // Ensure m_tip_block is set so consumers of BlockTemplate can rely on that.
        if (!waitTipChanged(uint256::ZERO, MillisecondsDouble::max())) 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);
    }

    bool checkBlock(const CBlock& block, const node::BlockCheckOptions& options, std::string& reason, std::string& debug) override
    {
        LOCK(chainman().GetMutex());
        BlockValidationState state{TestBlockValidity(chainman().ActiveChainstate(), block, /*check_pow=*/options.check_pow, /*=check_merkle_root=*/options.check_merkle_root)};
        reason = state.GetRejectReason();
        debug = state.GetDebugMessage();
        return state.IsValid();
    }

    NodeContext* context() override { return &m_node; }
    ChainstateManager& chainman() { return *Assert(m_node.chainman); }
    KernelNotifications& notifications() { return *Assert(m_node.notifications); }
    NodeContext& m_node;
};
} // namespace
} // namespace node

Review comment (bug):
In commit "mining: fix -blockreservedweight shadows IPC option" (336826ad0e70978783dcc0777ad1f01d3dbc1698)

It would seem better to raise an exception with an error message here instead of returning null, since this probably indicates a programming error, and it would be helpful to callers to know what specifically is wrong. (Also, this method is only currently documented to return null on shutdown.)

Separately, it would also would be good to enforce this limit in the mining code instead of the interfaces code, so all the limits are enforced in one place. But this could be something for the followup #33966

Code after:
};

class MinerImpl : public Mining
{
public:
    explicit MinerImpl(NodeContext& node) : m_node(node) {}

    bool isTestChain() override
    {
        return chainman().GetParams().IsTestChain();
    }

    bool isInitialBlockDownload() override
    {
        return chainman().IsInitialBlockDownload();
    }

    std::optional<BlockRef> getTip() override
    {
        return GetTip(chainman());
    }

    std::optional<BlockRef> waitTipChanged(uint256 current_tip, MillisecondsDouble timeout) override
    {
        return WaitTipChanged(chainman(), notifications(), current_tip, timeout);
    }

    std::unique_ptr<BlockTemplate> createNewBlock(const BlockCreateOptions& options) 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.
        if (!waitTipChanged(uint256::ZERO, MillisecondsDouble::max())) 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);
    }

    bool checkBlock(const CBlock& block, const node::BlockCheckOptions& options, std::string& reason, std::string& debug) override
    {
        LOCK(chainman().GetMutex());
        BlockValidationState state{TestBlockValidity(chainman().ActiveChainstate(), block, /*check_pow=*/options.check_pow, /*check_merkle_root=*/options.check_merkle_root)};