Coverage Report

Created: 2025-09-19 18:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/root/bitcoin/src/validationinterface.cpp
Line
Count
Source
1
// Copyright (c) 2009-2010 Satoshi Nakamoto
2
// Copyright (c) 2009-2022 The Bitcoin Core developers
3
// Distributed under the MIT software license, see the accompanying
4
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6
#include <validationinterface.h>
7
8
#include <chain.h>
9
#include <consensus/validation.h>
10
#include <kernel/chain.h>
11
#include <kernel/mempool_entry.h>
12
#include <kernel/mempool_removal_reason.h>
13
#include <logging.h>
14
#include <primitives/block.h>
15
#include <primitives/transaction.h>
16
#include <util/check.h>
17
#include <util/task_runner.h>
18
19
#include <future>
20
#include <memory>
21
#include <unordered_map>
22
#include <utility>
23
24
/**
25
 * ValidationSignalsImpl manages a list of shared_ptr<CValidationInterface> callbacks.
26
 *
27
 * A std::unordered_map is used to track what callbacks are currently
28
 * registered, and a std::list is used to store the callbacks that are
29
 * currently registered as well as any callbacks that are just unregistered
30
 * and about to be deleted when they are done executing.
31
 */
32
class ValidationSignalsImpl
33
{
34
private:
35
    Mutex m_mutex;
36
    //! List entries consist of a callback pointer and reference count. The
37
    //! count is equal to the number of current executions of that entry, plus 1
38
    //! if it's registered. It cannot be 0 because that would imply it is
39
    //! unregistered and also not being executed (so shouldn't exist).
40
    struct ListEntry { std::shared_ptr<CValidationInterface> callbacks; int count = 1; };
41
    std::list<ListEntry> m_list GUARDED_BY(m_mutex);
42
    std::unordered_map<CValidationInterface*, std::list<ListEntry>::iterator> m_map GUARDED_BY(m_mutex);
43
44
public:
45
    std::unique_ptr<util::TaskRunnerInterface> m_task_runner;
46
47
    explicit ValidationSignalsImpl(std::unique_ptr<util::TaskRunnerInterface> task_runner)
48
0
        : m_task_runner{std::move(Assert(task_runner))} {}
49
50
    void Register(std::shared_ptr<CValidationInterface> callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
51
0
    {
52
0
        LOCK(m_mutex);
53
0
        auto inserted = m_map.emplace(callbacks.get(), m_list.end());
54
0
        if (inserted.second) inserted.first->second = m_list.emplace(m_list.end());
55
0
        inserted.first->second->callbacks = std::move(callbacks);
56
0
    }
57
58
    void Unregister(CValidationInterface* callbacks) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
59
0
    {
60
0
        LOCK(m_mutex);
61
0
        auto it = m_map.find(callbacks);
62
0
        if (it != m_map.end()) {
63
0
            if (!--it->second->count) m_list.erase(it->second);
64
0
            m_map.erase(it);
65
0
        }
66
0
    }
67
68
    //! Clear unregisters every previously registered callback, erasing every
69
    //! map entry. After this call, the list may still contain callbacks that
70
    //! are currently executing, but it will be cleared when they are done
71
    //! executing.
72
    void Clear() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
73
0
    {
74
0
        LOCK(m_mutex);
75
0
        for (const auto& entry : m_map) {
76
0
            if (!--entry.second->count) m_list.erase(entry.second);
77
0
        }
78
0
        m_map.clear();
79
0
    }
80
81
    template<typename F> void Iterate(F&& f) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
82
0
    {
83
0
        WAIT_LOCK(m_mutex, lock);
84
0
        for (auto it = m_list.begin(); it != m_list.end();) {
85
0
            ++it->count;
86
0
            {
87
0
                REVERSE_LOCK(lock, m_mutex);
88
0
                f(*it->callbacks);
89
0
            }
90
0
            it = --it->count ? std::next(it) : m_list.erase(it);
91
0
        }
92
0
    }
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZZN17ValidationSignals15UpdatedBlockTipEPK11CBlockIndexS4_bENK3$_1clEvEUlR20CValidationInterfaceE_EEvOT_
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZN17ValidationSignals15ActiveTipChangeERK11CBlockIndexbE3$_0EEvOT_
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZZN17ValidationSignals25TransactionAddedToMempoolERK25NewMempoolTransactionInfomENK3$_1clEvEUlR20CValidationInterfaceE_EEvOT_
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZZN17ValidationSignals29TransactionRemovedFromMempoolERKSt10shared_ptrIK12CTransactionE20MemPoolRemovalReasonmENK3$_1clEvEUlR20CValidationInterfaceE_EEvOT_
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZZN17ValidationSignals14BlockConnectedE14ChainstateRoleRKSt10shared_ptrIK6CBlockEPK11CBlockIndexENK3$_1clEvEUlR20CValidationInterfaceE_EEvOT_
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZZN17ValidationSignals34MempoolTransactionsRemovedForBlockERKSt6vectorI29RemovedMempoolTransactionInfoSaIS3_EEjENK3$_1clEvEUlR20CValidationInterfaceE_EEvOT_
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZZN17ValidationSignals17BlockDisconnectedERKSt10shared_ptrIK6CBlockEPK11CBlockIndexENK3$_1clEvEUlR20CValidationInterfaceE_EEvOT_
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZZN17ValidationSignals17ChainStateFlushedE14ChainstateRoleRK13CBlockLocatorENK3$_1clEvEUlR20CValidationInterfaceE_EEvOT_
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZN17ValidationSignals12BlockCheckedERKSt10shared_ptrIK6CBlockERK20BlockValidationStateE3$_0EEvOT_
Unexecuted instantiation: validationinterface.cpp:_ZN21ValidationSignalsImpl7IterateIZN17ValidationSignals16NewPoWValidBlockEPK11CBlockIndexRKSt10shared_ptrIK6CBlockEE3$_0EEvOT_
93
};
94
95
ValidationSignals::ValidationSignals(std::unique_ptr<util::TaskRunnerInterface> task_runner)
96
0
    : m_internals{std::make_unique<ValidationSignalsImpl>(std::move(task_runner))} {}
97
98
1
ValidationSignals::~ValidationSignals() = default;
99
100
void ValidationSignals::FlushBackgroundCallbacks()
101
1
{
102
1
    m_internals->m_task_runner->flush();
103
1
}
104
105
size_t ValidationSignals::CallbacksPending()
106
0
{
107
0
    return m_internals->m_task_runner->size();
108
0
}
109
110
void ValidationSignals::RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
111
0
{
112
    // Each connection captures the shared_ptr to ensure that each callback is
113
    // executed before the subscriber is destroyed. For more details see #18338.
114
0
    m_internals->Register(std::move(callbacks));
115
0
}
116
117
void ValidationSignals::RegisterValidationInterface(CValidationInterface* callbacks)
118
0
{
119
    // Create a shared_ptr with a no-op deleter - CValidationInterface lifecycle
120
    // is managed by the caller.
121
0
    RegisterSharedValidationInterface({callbacks, [](CValidationInterface*){}});
122
0
}
123
124
void ValidationSignals::UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
125
0
{
126
0
    UnregisterValidationInterface(callbacks.get());
127
0
}
128
129
void ValidationSignals::UnregisterValidationInterface(CValidationInterface* callbacks)
130
0
{
131
0
    m_internals->Unregister(callbacks);
132
0
}
133
134
void ValidationSignals::UnregisterAllValidationInterfaces()
135
0
{
136
0
    m_internals->Clear();
137
0
}
138
139
void ValidationSignals::CallFunctionInValidationInterfaceQueue(std::function<void()> func)
140
0
{
141
0
    m_internals->m_task_runner->insert(std::move(func));
142
0
}
143
144
void ValidationSignals::SyncWithValidationInterfaceQueue()
145
0
{
146
0
    AssertLockNotHeld(cs_main);
147
    // Block until the validation queue drains
148
0
    std::promise<void> promise;
149
0
    CallFunctionInValidationInterfaceQueue([&promise] {
150
0
        promise.set_value();
151
0
    });
152
0
    promise.get_future().wait();
153
0
}
154
155
// Use a macro instead of a function for conditional logging to prevent
156
// evaluating arguments when logging is not enabled.
157
//
158
// NOTE: The lambda captures all local variables by value.
159
#define ENQUEUE_AND_LOG_EVENT(event, fmt, name, ...)           \
160
0
    do {                                                       \
161
0
        auto local_name = (name);                              \
162
0
        LOG_EVENT("Enqueuing " fmt, local_name, __VA_ARGS__);  \
163
0
        m_internals->m_task_runner->insert([=] { \
164
0
            LOG_EVENT(fmt, local_name, __VA_ARGS__);           \
165
0
            event();                                           \
166
0
        });                                                    \
Unexecuted instantiation: validationinterface.cpp:_ZZN17ValidationSignals15UpdatedBlockTipEPK11CBlockIndexS2_bENK3$_0clEv
Unexecuted instantiation: validationinterface.cpp:_ZZN17ValidationSignals25TransactionAddedToMempoolERK25NewMempoolTransactionInfomENK3$_0clEv
Unexecuted instantiation: validationinterface.cpp:_ZZN17ValidationSignals29TransactionRemovedFromMempoolERKSt10shared_ptrIK12CTransactionE20MemPoolRemovalReasonmENK3$_0clEv
Unexecuted instantiation: validationinterface.cpp:_ZZN17ValidationSignals14BlockConnectedE14ChainstateRoleRKSt10shared_ptrIK6CBlockEPK11CBlockIndexENK3$_0clEv
Unexecuted instantiation: validationinterface.cpp:_ZZN17ValidationSignals34MempoolTransactionsRemovedForBlockERKSt6vectorI29RemovedMempoolTransactionInfoSaIS1_EEjENK3$_0clEv
Unexecuted instantiation: validationinterface.cpp:_ZZN17ValidationSignals17BlockDisconnectedERKSt10shared_ptrIK6CBlockEPK11CBlockIndexENK3$_0clEv
Unexecuted instantiation: validationinterface.cpp:_ZZN17ValidationSignals17ChainStateFlushedE14ChainstateRoleRK13CBlockLocatorENK3$_0clEv
167
0
    } while (0)
168
169
#define LOG_EVENT(fmt, ...) \
170
0
    LogDebug(BCLog::VALIDATION, fmt "\n", __VA_ARGS__)
171
172
0
void ValidationSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
173
    // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which
174
    // the chain actually updates. One way to ensure this is for the caller to invoke this signal
175
    // in the same critical section where the chain is updated
176
177
0
    auto event = [pindexNew, pindexFork, fInitialDownload, this] {
178
0
        m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); });
179
0
    };
180
0
    ENQUEUE_AND_LOG_EVENT(event, "%s: new block hash=%s fork block hash=%s (in IBD=%s)", __func__,
181
0
                          pindexNew->GetBlockHash().ToString(),
182
0
                          pindexFork ? pindexFork->GetBlockHash().ToString() : "null",
183
0
                          fInitialDownload);
184
0
}
185
186
void ValidationSignals::ActiveTipChange(const CBlockIndex& new_tip, bool is_ibd)
187
0
{
188
0
    LOG_EVENT("%s: new block hash=%s block height=%d", __func__, new_tip.GetBlockHash().ToString(), new_tip.nHeight);
189
0
    m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ActiveTipChange(new_tip, is_ibd); });
190
0
}
191
192
void ValidationSignals::TransactionAddedToMempool(const NewMempoolTransactionInfo& tx, uint64_t mempool_sequence)
193
0
{
194
0
    auto event = [tx, mempool_sequence, this] {
195
0
        m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionAddedToMempool(tx, mempool_sequence); });
196
0
    };
197
0
    ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s", __func__,
198
0
                          tx.info.m_tx->GetHash().ToString(),
199
0
                          tx.info.m_tx->GetWitnessHash().ToString());
200
0
}
201
202
0
void ValidationSignals::TransactionRemovedFromMempool(const CTransactionRef& tx, MemPoolRemovalReason reason, uint64_t mempool_sequence) {
203
0
    auto event = [tx, reason, mempool_sequence, this] {
204
0
        m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.TransactionRemovedFromMempool(tx, reason, mempool_sequence); });
205
0
    };
206
0
    ENQUEUE_AND_LOG_EVENT(event, "%s: txid=%s wtxid=%s reason=%s", __func__,
207
0
                          tx->GetHash().ToString(),
208
0
                          tx->GetWitnessHash().ToString(),
209
0
                          RemovalReasonToString(reason));
210
0
}
211
212
0
void ValidationSignals::BlockConnected(ChainstateRole role, const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex) {
213
0
    auto event = [role, pblock, pindex, this] {
214
0
        m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockConnected(role, pblock, pindex); });
215
0
    };
216
0
    ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
217
0
                          pblock->GetHash().ToString(),
218
0
                          pindex->nHeight);
219
0
}
220
221
void ValidationSignals::MempoolTransactionsRemovedForBlock(const std::vector<RemovedMempoolTransactionInfo>& txs_removed_for_block, unsigned int nBlockHeight)
222
0
{
223
0
    auto event = [txs_removed_for_block, nBlockHeight, this] {
224
0
        m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.MempoolTransactionsRemovedForBlock(txs_removed_for_block, nBlockHeight); });
225
0
    };
226
0
    ENQUEUE_AND_LOG_EVENT(event, "%s: block height=%s txs removed=%s", __func__,
227
0
                          nBlockHeight,
228
0
                          txs_removed_for_block.size());
229
0
}
230
231
void ValidationSignals::BlockDisconnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex* pindex)
232
0
{
233
0
    auto event = [pblock, pindex, this] {
234
0
        m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockDisconnected(pblock, pindex); });
235
0
    };
236
0
    ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s block height=%d", __func__,
237
0
                          pblock->GetHash().ToString(),
238
0
                          pindex->nHeight);
239
0
}
240
241
0
void ValidationSignals::ChainStateFlushed(ChainstateRole role, const CBlockLocator &locator) {
242
0
    auto event = [role, locator, this] {
243
0
        m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.ChainStateFlushed(role, locator); });
244
0
    };
245
0
    ENQUEUE_AND_LOG_EVENT(event, "%s: block hash=%s", __func__,
246
0
                          locator.IsNull() ? "null" : locator.vHave.front().ToString());
247
0
}
248
249
void ValidationSignals::BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state)
250
0
{
251
0
    LOG_EVENT("%s: block hash=%s state=%s", __func__,
252
0
              block->GetHash().ToString(), state.ToString());
253
0
    m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockChecked(block, state); });
254
0
}
255
256
0
void ValidationSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
257
0
    LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
258
0
    m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.NewPoWValidBlock(pindex, block); });
259
0
}