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

Code before:
    }

    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())};

        if (!maybe_tip) return {};

        if (cooldown) {
            // Do not return a template during IBD, because it can have long
            // pauses and sometimes takes a while to get started. Although this
            // is useful in general, it's gated behind the cooldown argument,
            // because on regtest and single miner signets this would wait
            // forever if no block was mined in the past day.
            while (chainman().IsInitialBlockDownload()) {
                maybe_tip = waitTipChanged(maybe_tip->hash, MillisecondsDouble{1000});
                if (!maybe_tip) return {};
            }

            // Also wait during the final catch-up moments after IBD.
            if (!CooldownIfHeadersAhead(chainman(), notifications(), *maybe_tip)) 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); }

Review comment (suggestion):
In "mining: add cooldown argument to createNewBlock()" a11297a9048e0d910915e1a37b2be467c057a78d

IIUC, when a chainstate is loaded via `loadtxoutset`, the behaviour is that you keep connecting on top of the active loaded chainstate and cooldown based on it.


Code after:
    }

    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())};

        if (!maybe_tip) return {};

        if (cooldown) {
            // Do not return a template during IBD, because it can have long
            // pauses and sometimes takes a while to get started. Although this
            // is useful in general, it's gated behind the cooldown argument,
            // because on regtest and single miner signets this would wait
            // forever if no block was mined in the past day.
            while (chainman().IsInitialBlockDownload()) {
                maybe_tip = waitTipChanged(maybe_tip->hash, MillisecondsDouble{1000});
                if (!maybe_tip || chainman().m_interrupt || WITH_LOCK(notifications().m_tip_block_mutex, return m_interrupt_mining)) return {};
            }

            // 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);
    }

    void interrupt() override
    {
        InterruptWait(notifications(), m_interrupt_mining);
    }

    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();