Coverage Report

Created: 2024-08-21 05:08

/workdir/bitcoin/src/validationinterface.cpp
Line
Count
Source (jump to first uncovered line)
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 <logging.h>
13
#include <primitives/block.h>
14
#include <primitives/transaction.h>
15
#include <util/check.h>
16
#include <util/task_runner.h>
17
18
#include <future>
19
#include <unordered_map>
20
#include <utility>
21
22
std::string RemovalReasonToString(const MemPoolRemovalReason& r) noexcept;
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());
  Branch (54:13): [True: 0, False: 0]
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()) {
  Branch (62:13): [True: 0, False: 0]
63
0
            if (!--it->second->count) m_list.erase(it->second);
  Branch (63:17): [True: 0, False: 0]
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) {
  Branch (75:32): [True: 0, False: 0]
76
0
            if (!--entry.second->count) m_list.erase(entry.second);
  Branch (76:17): [True: 0, False: 0]
77
0
        }
78
0
        m_map.clear();
79
0
    }
80
81
    template<typename F> void Iterate(F&& f) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
82
6.84k
    {
83
6.84k
        WAIT_LOCK(m_mutex, lock);
84
6.84k
        for (auto it = m_list.begin(); it != m_list.end();) {
  Branch (84:40): [True: 0, False: 0]
  Branch (84:40): [True: 0, False: 0]
  Branch (84:40): [True: 0, False: 0]
  Branch (84:40): [True: 0, False: 0]
  Branch (84:40): [True: 0, False: 0]
  Branch (84:40): [True: 0, False: 0]
  Branch (84:40): [True: 0, False: 0]
  Branch (84:40): [True: 0, False: 0]
  Branch (84:40): [True: 0, False: 6.84k]
  Branch (84:40): [True: 0, False: 0]
85
0
            ++it->count;
86
0
            {
87
0
                REVERSE_LOCK(lock);
88
0
                f(*it->callbacks);
89
0
            }
90
0
            it = --it->count ? std::next(it) : m_list.erase(it);
  Branch (90:18): [True: 0, False: 0]
  Branch (90:18): [True: 0, False: 0]
  Branch (90:18): [True: 0, False: 0]
  Branch (90:18): [True: 0, False: 0]
  Branch (90:18): [True: 0, False: 0]
  Branch (90:18): [True: 0, False: 0]
  Branch (90:18): [True: 0, False: 0]
  Branch (90:18): [True: 0, False: 0]
  Branch (90:18): [True: 0, False: 0]
  Branch (90:18): [True: 0, False: 0]
91
0
        }
92
6.84k
    }
Unexecuted instantiation: validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::UpdatedBlockTip(CBlockIndex const*, CBlockIndex const*, bool)::$_1::operator()() const::{lambda(CValidationInterface&)#1}>(ValidationSignals::UpdatedBlockTip(CBlockIndex const*, CBlockIndex const*, bool)::$_1::operator()() const::{lambda(CValidationInterface&)#1}&&)
Unexecuted instantiation: validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::ActiveTipChange(CBlockIndex const&, bool)::$_0>(ValidationSignals::ActiveTipChange(CBlockIndex const&, bool)::$_0&&)
Unexecuted instantiation: validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::TransactionAddedToMempool(NewMempoolTransactionInfo const&, unsigned long)::$_1::operator()() const::{lambda(CValidationInterface&)#1}>(ValidationSignals::TransactionAddedToMempool(NewMempoolTransactionInfo const&, unsigned long)::$_1::operator()() const::{lambda(CValidationInterface&)#1}&&)
Unexecuted instantiation: validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::TransactionRemovedFromMempool(std::shared_ptr<CTransaction const> const&, MemPoolRemovalReason, unsigned long)::$_1::operator()() const::{lambda(CValidationInterface&)#1}>(ValidationSignals::TransactionRemovedFromMempool(std::shared_ptr<CTransaction const> const&, MemPoolRemovalReason, unsigned long)::$_1::operator()() const::{lambda(CValidationInterface&)#1}&&)
Unexecuted instantiation: validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::BlockConnected(ChainstateRole, std::shared_ptr<CBlock const> const&, CBlockIndex const*)::$_1::operator()() const::{lambda(CValidationInterface&)#1}>(ValidationSignals::BlockConnected(ChainstateRole, std::shared_ptr<CBlock const> const&, CBlockIndex const*)::$_1::operator()() const::{lambda(CValidationInterface&)#1}&&)
Unexecuted instantiation: validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::MempoolTransactionsRemovedForBlock(std::vector<RemovedMempoolTransactionInfo, std::allocator<RemovedMempoolTransactionInfo> > const&, unsigned int)::$_1::operator()() const::{lambda(CValidationInterface&)#1}>(ValidationSignals::MempoolTransactionsRemovedForBlock(std::vector<RemovedMempoolTransactionInfo, std::allocator<RemovedMempoolTransactionInfo> > const&, unsigned int)::$_1::operator()() const::{lambda(CValidationInterface&)#1}&&)
Unexecuted instantiation: validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::BlockDisconnected(std::shared_ptr<CBlock const> const&, CBlockIndex const*)::$_1::operator()() const::{lambda(CValidationInterface&)#1}>(ValidationSignals::BlockDisconnected(std::shared_ptr<CBlock const> const&, CBlockIndex const*)::$_1::operator()() const::{lambda(CValidationInterface&)#1}&&)
Unexecuted instantiation: validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::ChainStateFlushed(ChainstateRole, CBlockLocator const&)::$_1::operator()() const::{lambda(CValidationInterface&)#1}>(ValidationSignals::ChainStateFlushed(ChainstateRole, CBlockLocator const&)::$_1::operator()() const::{lambda(CValidationInterface&)#1}&&)
validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::BlockChecked(CBlock const&, BlockValidationState const&)::$_0>(ValidationSignals::BlockChecked(CBlock const&, BlockValidationState const&)::$_0&&)
Line
Count
Source
82
6.84k
    {
83
6.84k
        WAIT_LOCK(m_mutex, lock);
84
6.84k
        for (auto it = m_list.begin(); it != m_list.end();) {
  Branch (84:40): [True: 0, False: 6.84k]
85
0
            ++it->count;
86
0
            {
87
0
                REVERSE_LOCK(lock);
88
0
                f(*it->callbacks);
89
0
            }
90
0
            it = --it->count ? std::next(it) : m_list.erase(it);
  Branch (90:18): [True: 0, False: 0]
91
0
        }
92
6.84k
    }
Unexecuted instantiation: validationinterface.cpp:void ValidationSignalsImpl::Iterate<ValidationSignals::NewPoWValidBlock(CBlockIndex const*, std::shared_ptr<CBlock const> const&)::$_0>(ValidationSignals::NewPoWValidBlock(CBlockIndex const*, std::shared_ptr<CBlock const> const&)::$_0&&)
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
290
{
141
290
    m_internals->m_task_runner->insert(std::move(func));
142
290
}
143
144
void ValidationSignals::SyncWithValidationInterfaceQueue()
145
290
{
146
290
    AssertLockNotHeld(cs_main);
147
    // Block until the validation queue drains
148
290
    std::promise<void> promise;
149
290
    CallFunctionInValidationInterfaceQueue([&promise] {
150
290
        promise.set_value();
151
290
    });
152
290
    promise.get_future().wait();
153
290
}
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:ValidationSignals::UpdatedBlockTip(CBlockIndex const*, CBlockIndex const*, bool)::$_0::operator()() const
Unexecuted instantiation: validationinterface.cpp:ValidationSignals::TransactionAddedToMempool(NewMempoolTransactionInfo const&, unsigned long)::$_0::operator()() const
Unexecuted instantiation: validationinterface.cpp:ValidationSignals::TransactionRemovedFromMempool(std::shared_ptr<CTransaction const> const&, MemPoolRemovalReason, unsigned long)::$_0::operator()() const
Unexecuted instantiation: validationinterface.cpp:ValidationSignals::BlockConnected(ChainstateRole, std::shared_ptr<CBlock const> const&, CBlockIndex const*)::$_0::operator()() const
Unexecuted instantiation: validationinterface.cpp:ValidationSignals::MempoolTransactionsRemovedForBlock(std::vector<RemovedMempoolTransactionInfo, std::allocator<RemovedMempoolTransactionInfo> > const&, unsigned int)::$_0::operator()() const
Unexecuted instantiation: validationinterface.cpp:ValidationSignals::BlockDisconnected(std::shared_ptr<CBlock const> const&, CBlockIndex const*)::$_0::operator()() const
Unexecuted instantiation: validationinterface.cpp:ValidationSignals::ChainStateFlushed(ChainstateRole, CBlockLocator const&)::$_0::operator()() const
167
0
    } while (0)
168
169
#define LOG_EVENT(fmt, ...) \
170
6.84k
    LogPrint(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
6.84k
void ValidationSignals::BlockChecked(const CBlock& block, const BlockValidationState& state) {
250
6.84k
    LOG_EVENT("%s: block hash=%s state=%s", __func__,
251
6.84k
              block.GetHash().ToString(), state.ToString());
252
6.84k
    m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.BlockChecked(block, state); });
253
6.84k
}
254
255
0
void ValidationSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
256
0
    LOG_EVENT("%s: block hash=%s", __func__, block->GetHash().ToString());
257
0
    m_internals->Iterate([&](CValidationInterface& callbacks) { callbacks.NewPoWValidBlock(pindex, block); });
258
0
}