• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-blockchain / 8117832118

01 Mar 2024 10:15PM CUT coverage: 37.046%. Remained the same
8117832118

push

github

web-flow
Merge pull request #554 from pmienk/version3

Regenerate artifacts.

592 of 1598 relevant lines covered (37.05%)

11.49 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

6.47
/src/validate/validate_block.cpp
1
/**
2
 * Copyright (c) 2011-2023 libbitcoin developers (see AUTHORS)
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <bitcoin/blockchain/validate/validate_block.hpp>
20

21
#include <algorithm>
22
#include <cstddef>
23
#include <cstdint>
24
#include <functional>
25
#include <memory>
26
#include <bitcoin/system.hpp>
27
#include <bitcoin/blockchain/interface/fast_chain.hpp>
28
#include <bitcoin/blockchain/pools/branch.hpp>
29
#include <bitcoin/blockchain/settings.hpp>
30
#include <bitcoin/blockchain/validate/validate_input.hpp>
31

32
namespace libbitcoin {
33
namespace blockchain {
34

35
using namespace bc::chain;
36
using namespace bc::machine;
37
using namespace std::placeholders;
38

39
#define NAME "validate_block"
40

41
// Database access is limited to: populator:
42
// spend: { spender }
43
// block: { bits, version, timestamp }
44
// transaction: { exists, height, output }
45

46
// If the priority threadpool is shut down when this is running the handlers
47
// will never be invoked, resulting in a threadpool.join indefinite hang.
48

49
validate_block::validate_block(dispatcher& dispatch, const fast_chain& chain,
43✔
50
    const settings& settings, bool relay_transactions)
43✔
51
  : stopped_(true),
52
    use_libconsensus_(settings.use_libconsensus),
43✔
53
    fast_chain_(chain),
54
    priority_dispatch_(dispatch),
55
    block_populator_(dispatch, chain, relay_transactions)
43✔
56
{
57
}
43✔
58

59
// Start/stop sequences.
60
//-----------------------------------------------------------------------------
61

62
void validate_block::start()
42✔
63
{
64
    stopped_ = false;
42✔
65
}
42✔
66

67
void validate_block::stop()
43✔
68
{
69
    stopped_ = true;
43✔
70
}
43✔
71

72
// Check.
73
//-----------------------------------------------------------------------------
74
// These checks are context free.
75

76
void validate_block::check(block_const_ptr block, result_handler handler) const
×
77
{
78
    // The block hasn't been checked yet.
79
    if (block->transactions().empty())
×
80
    {
81
        handler(error::success);
×
82
        return;
×
83
    }
84

85
    result_handler complete_handler =
×
86
        std::bind(&validate_block::handle_checked,
×
87
            this, _1, block, handler);
×
88

89
    // TODO: make configurable for each parallel segment.
90
    // This one is more efficient with one thread than parallel.
91
    const auto threads = std::min(size_t(1), priority_dispatch_.size());
×
92

93
    const auto count = block->transactions().size();
×
94
    const auto buckets = std::min(threads, count);
×
95
    BITCOIN_ASSERT(buckets != 0);
×
96

97
    const auto join_handler = synchronize(std::move(complete_handler), buckets,
×
98
        NAME "_check");
×
99

100
    for (size_t bucket = 0; bucket < buckets; ++bucket)
×
101
        priority_dispatch_.concurrent(&validate_block::check_block,
×
102
            this, block, bucket, buckets, join_handler);
103
}
104

105
void validate_block::check_block(block_const_ptr block, size_t bucket,
×
106
    size_t buckets, result_handler handler) const
107
{
108
    if (stopped())
×
109
    {
110
        handler(error::service_stopped);
×
111
        return;
×
112
    }
113

114
    const auto& txs = block->transactions();
×
115

116
    // Generate each tx hash (stored in tx cache).
117
    for (auto tx = bucket; tx < txs.size(); tx = ceiling_add(tx, buckets))
×
118
        txs[tx].hash();
×
119

120
    handler(error::success);
×
121
}
122

123
void validate_block::handle_checked(const code& ec, block_const_ptr block,
×
124
    result_handler handler) const
125
{
126
    if (ec)
×
127
    {
128
        handler(ec);
×
129
        return;
×
130
    }
131

132
    // Run context free checks, sets time internally.
133
    handler(block->check());
×
134
}
135

136
// Accept sequence.
137
//-----------------------------------------------------------------------------
138
// These checks require chain state, and block state if not under checkpoint.
139

140
void validate_block::accept(branch::const_ptr branch,
×
141
    result_handler handler) const
142
{
143
    const auto block = branch->top();
×
144
    BITCOIN_ASSERT(block);
×
145

146
    // The block has no population timer, so set externally.
147
    block->validation.start_populate = asio::steady_clock::now();
×
148

149
    // Populate chain state for the next block.
150
    block->validation.state = fast_chain_.chain_state(branch);
×
151

152
    if (!block->validation.state)
×
153
    {
154
        handler(error::operation_failed);
×
155
        return;
×
156
    }
157

158
    // Populate block state for the top block (others are valid).
159
    block_populator_.populate(branch,
×
160
        std::bind(&validate_block::handle_populated,
×
161
            this, _1, block, handler));
162
}
163

164
void validate_block::handle_populated(const code& ec, block_const_ptr block,
×
165
    result_handler handler) const
166
{
167
    if (stopped())
×
168
    {
169
        handler(error::service_stopped);
×
170
        return;
×
171
    }
172

173
    if (ec)
×
174
    {
175
        handler(ec);
×
176
        return;
×
177
    }
178

179
    // Run contextual block non-tx checks (sets start time).
180
    const auto error_code = block->accept(false);
×
181

182
    if (error_code)
×
183
    {
184
        handler(error_code);
×
185
        return;
×
186
    }
187

188
    const auto sigops = std::make_shared<atomic_counter>(0);
×
189
    const auto state = block->validation.state;
×
190
    BITCOIN_ASSERT(state);
×
191

192
    const auto bip141 = state->is_enabled(rule_fork::bip141_rule);
×
193

194
    result_handler complete_handler =
×
195
        std::bind(&validate_block::handle_accepted,
×
196
            this, _1, block, sigops, bip141, handler);
×
197

198
    if (state->is_under_checkpoint())
×
199
    {
200
        complete_handler(error::success);
×
201
        return;
×
202
    }
203

204
    const auto count = block->transactions().size();
×
205
    const auto bip16 = state->is_enabled(rule_fork::bip16_rule);
×
206
    const auto buckets = std::min(priority_dispatch_.size(), count);
×
207
    BITCOIN_ASSERT(buckets != 0);
×
208

209
    const auto join_handler = synchronize(std::move(complete_handler), buckets,
×
210
        NAME "_accept");
×
211

212
    for (size_t bucket = 0; bucket < buckets; ++bucket)
×
213
        priority_dispatch_.concurrent(&validate_block::accept_transactions,
×
214
            this, block, bucket, buckets, sigops, bip16, bip141, join_handler);
215
}
216

217
void validate_block::accept_transactions(block_const_ptr block, size_t bucket,
×
218
    size_t buckets, atomic_counter_ptr sigops, bool bip16, bool bip141,
219
    result_handler handler) const
220
{
221
    if (stopped())
×
222
    {
223
        handler(error::service_stopped);
×
224
        return;
×
225
    }
226

227
    code ec(error::success);
×
228
    const auto& state = *block->validation.state;
×
229
    const auto& txs = block->transactions();
×
230
    const auto count = txs.size();
×
231

232
    // Run contextual tx non-script checks (not in tx order).
233
    for (auto tx = bucket; tx < count && !ec; tx = ceiling_add(tx, buckets))
×
234
    {
235
        const auto& transaction = txs[tx];
×
236
        ec = transaction.accept(state, false);
×
237
        *sigops += transaction.signature_operations(bip16, bip141);
×
238
    }
239

240
    handler(ec);
×
241
}
242

243
void validate_block::handle_accepted(const code& ec, block_const_ptr block,
×
244
    atomic_counter_ptr sigops, bool bip141, result_handler handler) const
245
{
246
    if (ec)
×
247
    {
248
        handler(ec);
×
249
        return;
×
250
    }
251

252
    const auto max_sigops = bip141 ? max_fast_sigops : max_block_sigops;
×
253
    const auto exceeded = *sigops > max_sigops;
×
254
    handler(exceeded ? error::block_embedded_sigop_limit : error::success);
×
255
}
256

257
// Connect sequence.
258
//-----------------------------------------------------------------------------
259
// These checks require chain state, block state and perform script validation.
260

261
void validate_block::connect(branch::const_ptr branch,
×
262
    result_handler handler) const
263
{
264
    const auto block = branch->top();
×
265
    BITCOIN_ASSERT(block && block->validation.state);
×
266

267
    // We are reimplementing connect, so must set timer externally.
268
    block->validation.start_connect = asio::steady_clock::now();
×
269

270
    if (block->validation.state->is_under_checkpoint())
×
271
    {
272
        handler(error::success);
×
273
        return;
×
274
    }
275

276
    const auto non_coinbase_inputs = block->total_inputs(false);
×
277

278
    // Return if there are no non-coinbase inputs to validate.
279
    if (non_coinbase_inputs == 0)
×
280
    {
281
        handler(error::success);
×
282
        return;
×
283
    }
284

285
    // Reset statistics for each block (treat coinbase as cached).
286
    hits_ = 0;
×
287
    queries_ = 0;
×
288

289
    result_handler complete_handler =
×
290
        std::bind(&validate_block::handle_connected,
×
291
            this, _1, block, handler);
×
292

293
    const auto threads = priority_dispatch_.size();
×
294
    const auto buckets = std::min(threads, non_coinbase_inputs);
×
295
    BITCOIN_ASSERT(buckets != 0);
×
296

297
    const auto join_handler = synchronize(std::move(complete_handler), buckets,
×
298
        NAME "_validate");
×
299

300
    for (size_t bucket = 0; bucket < buckets; ++bucket)
×
301
        priority_dispatch_.concurrent(&validate_block::connect_inputs,
×
302
            this, block, bucket, buckets, join_handler);
303
}
304

305
void validate_block::connect_inputs(block_const_ptr block, size_t bucket,
×
306
    size_t buckets, result_handler handler) const
307
{
308
    BITCOIN_ASSERT(bucket < buckets);
×
309
    code ec(error::success);
×
310
    const auto forks = block->validation.state->enabled_forks();
×
311
    const auto& txs = block->transactions();
×
312
    size_t position = 0;
×
313

314
    // Must skip coinbase here as it is already accounted for.
315
    for (auto tx = txs.begin() + 1; tx != txs.end(); ++tx)
×
316
    {
317
        ++queries_;
×
318

319
        // The tx is pooled with current fork state so outputs are validated.
320
        if (tx->validation.current)
×
321
        {
322
            ++hits_;
×
323
            continue;
×
324
        }
325

326
        size_t input_index;
×
327
        const auto& inputs = tx->inputs();
×
328

329
        for (input_index = 0; input_index < inputs.size();
×
330
            ++input_index, ++position)
331
        {
332
            if (position % buckets != bucket)
×
333
                continue;
×
334

335
            if (stopped())
×
336
            {
337
                handler(error::service_stopped);
×
338
                return;
×
339
            }
340

341
            const auto& prevout = inputs[input_index].previous_output();
×
342

343
            if (!prevout.validation.cache.is_valid())
×
344
            {
345
                ec = error::missing_previous_output;
×
346
                break;
347
            }
348

349
            if ((ec = validate_input::verify_script(*tx, input_index, forks,
×
350
                use_libconsensus_)))
×
351
            {
352
                break;
353
            }
354
        }
355

356
        if (ec)
×
357
        {
358
            const auto height = block->validation.state->height();
×
359
            dump(ec, *tx, input_index, forks, height, use_libconsensus_);
×
360
            break;
361
        }
362
    }
363

364
    handler(ec);
×
365
}
366

367
// The tx pool cache hit rate.
368
float validate_block::hit_rate() const
×
369
{
370
    // These values could overflow or divide by zero, but that's okay.
371
    return queries_ == 0 ? 0.0f : (hits_ * 1.0f / queries_);
×
372
}
373

374
void validate_block::handle_connected(const code& ec, block_const_ptr block,
×
375
    result_handler handler) const
376
{
377
    block->validation.cache_efficiency = hit_rate();
×
378
    handler(ec);
×
379
}
×
380

381
// Utility.
382
//-----------------------------------------------------------------------------
383

384
void validate_block::dump(const code& ec, const transaction& tx,
×
385
    uint32_t input_index, uint32_t forks, size_t height, bool use_libconsensus)
386
{
387
    const auto& prevout = tx.inputs()[input_index].previous_output();
×
388
    const auto script = prevout.validation.cache.script().to_data(false);
×
389
    const auto hash = encode_hash(prevout.hash());
×
390
    const auto tx_hash = encode_hash(tx.hash());
×
391

392
    LOG_DEBUG(LOG_BLOCKCHAIN)
×
393
        << "Verify failed [" << height << "] : " << ec.message() << std::endl
×
394
        << " libconsensus : " << use_libconsensus << std::endl
×
395
        << " forks        : " << forks << std::endl
×
396
        << " outpoint     : " << hash << ":" << prevout.index() << std::endl
×
397
        << " script       : " << encode_base16(script) << std::endl
×
398
        << " value        : " << prevout.validation.cache.value() << std::endl
×
399
        << " inpoint      : " << tx_hash << ":" << input_index << std::endl
×
400
        << " transaction  : " << encode_base16(tx.to_data(true, true));
×
401
}
×
402

403
} // namespace blockchain
404
} // namespace libbitcoin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc