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

libbitcoin / libbitcoin-system / 14434326165

13 Apr 2025 11:21PM UTC coverage: 82.798% (-0.1%) from 82.939%
14434326165

push

github

web-flow
Merge pull request #1638 from evoskuil/master

Move golomb_coding from /crypto to new /filter folder.

62 of 64 new or added lines in 13 files covered. (96.88%)

32 existing lines in 9 files now uncovered.

10190 of 12307 relevant lines covered (82.8%)

3818174.07 hits per line

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

87.0
/src/chain/operation.cpp
1

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

22
#include <algorithm>
23
#include <memory>
24
#include <bitcoin/system/chain/enums/numbers.hpp>
25
#include <bitcoin/system/chain/enums/opcode.hpp>
26
#include <bitcoin/system/data/data.hpp>
27
#include <bitcoin/system/define.hpp>
28
#include <bitcoin/system/machine/machine.hpp>
29
#include <bitcoin/system/math/math.hpp>
30
#include <bitcoin/system/serial/serial.hpp>
31
#include <bitcoin/system/unicode/unicode.hpp>
32

33
namespace libbitcoin {
34
namespace system {
35
namespace chain {
36

37
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
38

39
// Gotta set something when invalid minimal result, test is_valid.
40
static constexpr auto any_invalid = opcode::op_xor;
41

42
// Null data helpers.
43
// ----------------------------------------------------------------------------
44

45
// static/private
46
const data_chunk& operation::no_data() NOEXCEPT
153✔
47
{
48
    static const data_chunk empty_data{};
154✔
49
    return empty_data;
153✔
50
}
51

52
// static/private
53
const chunk_cptr& operation::no_data_cptr() NOEXCEPT
×
54
{
55
    BC_PUSH_WARNING(NO_NEW_OR_DELETE)
56
    static const std::shared_ptr<const data_chunk> empty
×
57
    {
58
        new const data_chunk{}
×
59
    };
×
60
    BC_POP_WARNING()
61
    return empty;
×
62
}
63

64
// static/private
65
// Push data is not possible with an invalid code, combination is invalid.
66
const chunk_cptr& operation::any_data_cptr() NOEXCEPT
10✔
67
{
68
    BC_PUSH_WARNING(NO_NEW_OR_DELETE)
69
    static const std::shared_ptr<const data_chunk> any
10✔
70
    {
71
        new const data_chunk{ 0x42 }
2✔
72
    };
11✔
73
    BC_POP_WARNING()
74
    return any;
10✔
75
}
76

77
bool operation::data_empty() const NOEXCEPT
82✔
78
{
79
    return !data_ || data_->empty();
82✔
80
}
81

82
size_t operation::data_size() const NOEXCEPT
1,529,548✔
83
{
84
    return data_ ? data_->size() : zero;
1,529,548✔
85
}
86

87
const data_chunk& operation::get_data() const NOEXCEPT
1,492,376✔
88
{
89
    return data_ ? *data_ : no_data();
1,492,376✔
90
}
91

92
const chunk_cptr& operation::get_data_cptr() const NOEXCEPT
4,474✔
93
{
94
    return data_ ? data_ : no_data_cptr();
4,474✔
95
}
96

97
// Constructors.
98
// ----------------------------------------------------------------------------
99

100
operation::operation() NOEXCEPT
10✔
101
  : operation(any_invalid, any_data_cptr(), false)
10✔
102
{
103
}
10✔
104

105
// If code is push data the data member will be inconsistent (empty).
106
operation::operation(opcode code) NOEXCEPT
452✔
107
  : operation(code, nullptr, false)
452✔
108
{
109
}
452✔
110

111
operation::operation(data_chunk&& push_data, bool minimal) NOEXCEPT
44✔
112
  : operation(from_push_data(to_shared(std::move(push_data)), minimal))
44✔
113
{
114
}
44✔
115

116
operation::operation(const data_chunk& push_data, bool minimal) NOEXCEPT
5✔
117
  : operation(from_push_data(to_shared(push_data), minimal))
5✔
118
{
119
}
5✔
120

121
operation::operation(const chunk_cptr& push_data, bool minimal) NOEXCEPT
×
122
  : operation(from_push_data(push_data, minimal))
×
123
{
124
}
×
125

126
operation::operation(stream::in::fast&& stream) NOEXCEPT
35✔
127
  : operation(read::bytes::fast(stream))
35✔
128
{
129
}
35✔
130

131
operation::operation(stream::in::fast& stream) NOEXCEPT
1✔
132
  : operation(read::bytes::fast(stream))
1✔
133
{
134
}
1✔
135

UNCOV
136
operation::operation(std::istream&& stream) NOEXCEPT
×
UNCOV
137
  : operation(read::bytes::istream(stream))
×
138
{
UNCOV
139
}
×
140

141
operation::operation(std::istream& stream) NOEXCEPT
4✔
142
  : operation(read::bytes::istream(stream))
4✔
143
{
144
}
4✔
145

146
operation::operation(reader&& source) NOEXCEPT
40✔
147
  : operation(source)
40✔
148
{
149
}
×
150

151
operation::operation(reader& source) NOEXCEPT
82,733✔
152
{
153
    assign_data(source);
82,733✔
154
}
82,733✔
155

156
operation::operation(const std::string& mnemonic) NOEXCEPT
10,738✔
157
  : operation(from_string(mnemonic))
10,738✔
158
{
159
}
10,738✔
160

161
// protected
162
operation::operation(opcode code, const chunk_cptr& push_data,
11,243✔
163
    bool underflow) NOEXCEPT
462✔
164
  : code_(code), data_(push_data), underflow_(underflow)
11,243✔
165
{
166
}
×
167

168
// Operators.
169
// ----------------------------------------------------------------------------
170

171
bool operation::operator==(const operation& other) const NOEXCEPT
244✔
172
{
173
    return (code_ == other.code_)
244✔
174
        && (data_ == other.data_ || get_data() == other.get_data())
234✔
175
        && (underflow_ == other.underflow_);
478✔
176
}
177

178
bool operation::operator!=(const operation& other) const NOEXCEPT
2✔
179
{
180
    return !(*this == other);
2✔
181
}
182

183
// Deserialization.
184
// ----------------------------------------------------------------------------
185

186
// private
187
void operation::assign_data(reader& source) NOEXCEPT
82,733✔
188
{
189
    auto& allocator = source.get_allocator();
82,733✔
190

191
    // Guard against resetting a previously-invalid stream.
192
    if (!source)
82,733✔
193
    {
194
        INPLACE(&data_, data_chunk, allocator, nullptr);
×
195
        return;
82,722✔
196
    }
197

198
    // If stream is not empty then a non-data opcode will always deserialize.
199
    // A push-data opcode may indicate more bytes than are available. In this
200
    // case the the script is invalid, but it may not be evaluated, such as
201
    // with a coinbase input. So if an operation fails to deserialize it is
202
    // re-read and retained as an "underflow" operation. An underflow op
203
    // serializes as data only, and fails evaluation. Only the last operation
204
    // in a script could become an underflow, which may possibly contain the
205
    // entire script. This retains the read position in case of underflow.
206
    const auto start = source.get_read_position();
82,733✔
207

208
    // Size of a push-data opcode is not retained, as this is inherent in data.
209
    code_ = static_cast<opcode>(source.read_byte());
82,733✔
210
    const auto size = read_data_size(code_, source);
82,733✔
211

212
    // read_bytes only guarded from excessive allocation by stream limit.
213
    if (size > max_block_size)
82,733✔
214
        source.invalidate();
1✔
215

216
    // An invalid source.read_bytes_raw returns nullptr.
217
    const auto ptr = source.read_bytes_raw(size);
82,733✔
218
    underflow_ = !source;
82,733✔
219
    if (!underflow_)
82,733✔
220
    {
221
        INPLACE(&data_, data_chunk, allocator, ptr);
82,722✔
222
        return;
82,722✔
223
    }
224

225
    // This requires that provided stream terminates at the end of the script.
226
    // When passing ops as part of a stream longer than the script, such as for
227
    // a transaction, caller should apply source.set_limit(prefix_size), and
228
    // clear the stream limit upon return. Stream invalidation and set_position
229
    // do not alter a stream limit, it just behaves as a smaller stream buffer.
230
    // Without a limit, source.read_bytes() below consumes the remaining stream.
231
    code_ = any_invalid;
11✔
232
    source.set_position(start);
11✔
233
    INPLACE(&data_, data_chunk, allocator, source.read_bytes_raw());
11✔
234
}
235

236
// static/private
237
operation operation::from_push_data(const chunk_cptr& data,
49✔
238
    bool minimal) NOEXCEPT
239
{
240
    const auto code = opcode_from_data(*data, minimal);
49✔
241

242
    // Minimal interpretation affects only single byte push data.
243
    // Revert data if (minimal) opcode_from_data produced a numeric encoding.
244
    const auto push = is_payload(code) ? data : nullptr;
49✔
245

246
    return { code, push, false };
49✔
247
}
248

249
inline bool is_push_token(const std::string& token) NOEXCEPT
10,738✔
250
{
251
    return token.size() > one && token.front() == '[' && token.back() == ']';
10,738✔
252
}
253

254
inline bool is_text_token(const std::string& token) NOEXCEPT
10,579✔
255
{
256
    return token.size() > one && token.front() == '\'' && token.back() == '\'';
10,579✔
257
}
258

259
inline bool is_underflow_token(const std::string& token) NOEXCEPT
9,334✔
260
{
261
    return token.size() > one && token.front() == '<' && token.back() == '>';
9,334✔
262
}
263

264
inline std::string remove_token_delimiters(const std::string& token) NOEXCEPT
1,419✔
265
{
266
    BC_ASSERT(token.size() > one);
1,419✔
267
    return std::string(std::next(token.begin()), std::prev(token.end()));
1,419✔
268
}
269

270
inline string_list split_push_token(const std::string& token) NOEXCEPT
159✔
271
{
272
    return split(remove_token_delimiters(token), ".", false, false);
318✔
273
}
274

275
static bool opcode_from_data_prefix(opcode& out_code,
23✔
276
    const std::string& prefix, const data_chunk& push_data) NOEXCEPT
277
{
278
    constexpr auto op_75 = static_cast<uint8_t>(opcode::push_size_75);
23✔
279
    const auto size = push_data.size();
23✔
280
    out_code = operation::opcode_from_size(size);
23✔
281

282
    if (prefix == "0")
23✔
283
    {
284
        return size <= op_75;
2✔
285
    }
286
    else if (prefix == "1")
21✔
287
    {
288
        out_code = opcode::push_one_size;
11✔
289
        return size <= max_uint8;
11✔
290
    }
291
    else if (prefix == "2")
10✔
292
    {
293
        out_code = opcode::push_two_size;
5✔
294
        return size <= max_uint16;
5✔
295
    }
296
    else if (prefix == "4")
5✔
297
    {
298
        out_code = opcode::push_four_size;
3✔
299
        return size <= max_uint32;
3✔
300
    }
301

302
    return false;
303
}
304

305
static bool data_from_decimal(data_chunk& out_data,
296✔
306
    const std::string& token) NOEXCEPT
307
{
308
    // Deserialization to a number can convert random text to zero.
309
    if (!is_ascii_numeric(token))
296✔
310
        return false;
311

312
    int64_t value;
296✔
313
    if (!deserialize(value, token))
296✔
314
        return false;
315

316
    out_data = machine::number::chunk::from_integer(value);
296✔
317
    return true;
296✔
318
}
319

320
// private/static
321
operation operation::from_string(const std::string& mnemonic) NOEXCEPT
10,738✔
322
{
323
    data_chunk chunk;
10,738✔
324
    auto valid = false;
10,738✔
325
    auto underflow = false;
10,738✔
326

327
    // Always defined below, but this fixes warning.
328
    opcode code{ opcode::op_xor };
10,738✔
329

330
    if (is_push_token(mnemonic))
10,738✔
331
    {
332
        // Data encoding uses single token with one or two parts.
333
        const auto parts = split_push_token(mnemonic);
159✔
334

335
        if (parts.size() == one)
159✔
336
        {
337
            // Extract operation using nominal data size decoding.
338
            if ((valid = decode_base16(chunk, parts.front())))
133✔
339
                code = nominal_opcode_from_data(chunk);
133✔
340
        }
341
        else if (parts.size() == two)
26✔
342
        {
343
            // Extract operation using explicit data size decoding.
344

345
            // More efficient [] dereference is guarded above.
346
            BC_PUSH_WARNING(NO_ARRAY_INDEXING)
347
            valid = decode_base16(chunk, parts[1]) &&
49✔
348
                opcode_from_data_prefix(code, parts[0], chunk);
23✔
349
            BC_POP_WARNING()
350
        }
351
    }
159✔
352
    else if (is_text_token(mnemonic))
10,579✔
353
    {
354
        // Extract operation using nominal data size decoding.
355
        chunk = to_chunk(remove_token_delimiters(mnemonic));
1,245✔
356
        code = nominal_opcode_from_data(chunk);
1,245✔
357
        valid = true;
1,245✔
358
    }
359
    else if (is_underflow_token(mnemonic))
9,334✔
360
    {
361
        // code is ignored for underflow ops.
362
        underflow = true;
2✔
363
        code = any_invalid;
2✔
364
        valid = decode_base16(chunk, remove_token_delimiters(mnemonic));
4✔
365
    }
366
    else if (opcode_from_mnemonic(code, mnemonic))
9,332✔
367
    {
368
        // Any push code may have empty data, so this is presumed here.
369
        // No data is obtained here from a push opcode (use push/text tokens).
370
        valid = true;
371
    }
372
    else if (data_from_decimal(chunk, mnemonic))
296✔
373
    {
374
        // opcode_from_mnemonic captures [-1, 0, 1..16] integers, others here.
375
        code = nominal_opcode_from_data(chunk);
296✔
376
        valid = true;
296✔
377
    }
378

379
    if (!valid)
10,738✔
380
        return {};
6✔
381

382
    return { code, to_shared(std::move(chunk)), underflow };
21,464✔
383
}
384

385
// Serialization.
386
// ----------------------------------------------------------------------------
387

388
data_chunk operation::to_data() const NOEXCEPT
24✔
389
{
390
    data_chunk data(serialized_size());
24✔
391
    stream::out::fast ostream(data);
24✔
392
    write::bytes::fast out(ostream);
24✔
393
    to_data(out);
24✔
394
    return data;
24✔
395
}
24✔
396

397
void operation::to_data(std::ostream& stream) const NOEXCEPT
1✔
398
{
399
    write::bytes::ostream out(stream);
1✔
400
    to_data(out);
1✔
401
}
1✔
402

403
void operation::to_data(writer& sink) const NOEXCEPT
1,491,443✔
404
{
405
    // Underflow is op-undersized data, it is serialized with no opcode.
406
    // An underflow could only be a final token in a script deserialization.
407
    if (is_underflow())
1,491,443✔
408
    {
409
        sink.write_bytes(get_data());
14✔
410
    }
411
    else
412
    {
413
        const auto size = data_size();
1,491,429✔
414
        sink.write_byte(static_cast<uint8_t>(code_));
1,491,429✔
415

416
        switch (code_)
1,491,429✔
417
        {
418
            case opcode::push_one_size:
2✔
419
                sink.write_byte(narrow_cast<uint8_t>(size));
2✔
420
                break;
2✔
421
            case opcode::push_two_size:
3✔
422
                sink.write_2_bytes_little_endian(narrow_cast<uint16_t>(size));
3✔
423
                break;
3✔
424
            case opcode::push_four_size:
2✔
425
                sink.write_4_bytes_little_endian(
2✔
426
                    possible_narrow_cast<uint32_t>(size));
427
                break;
2✔
428
            default:
429
            break;
430
        }
431

432
        sink.write_bytes(get_data());
1,491,429✔
433
    }
434
}
1,491,443✔
435

436
// To String.
437
// ----------------------------------------------------------------------------
438

439
static std::string opcode_to_prefix(opcode code,
16✔
440
    const data_chunk& data) NOEXCEPT
441
{
442
    // If opcode is minimal for a size-based encoding, do not set a prefix.
443
    if (code == operation::opcode_from_size(data.size()))
16✔
444
        return "";
13✔
445

446
    switch (code)
3✔
447
    {
448
        case opcode::push_one_size:
1✔
449
            return "1.";
1✔
450
        case opcode::push_two_size:
1✔
451
            return "2.";
1✔
452
        case opcode::push_four_size:
1✔
453
            return "4.";
1✔
454
        default:
×
455
            return "0.";
×
456
    }
457
}
458

459
std::string operation::to_string(uint32_t active_flags) const NOEXCEPT
77✔
460
{
461
    if (!is_valid())
77✔
462
        return "(?)";
×
463

464
    if (underflow_)
77✔
465
        return "<" + encode_base16(get_data()) + ">";
4✔
466

467
    if (data_empty())
75✔
468
        return opcode_to_mnemonic(code_, active_flags);
59✔
469

470
    // Data encoding uses single token with explicit size prefix as required.
471
    return "[" + opcode_to_prefix(code_, get_data()) +
32✔
472
        encode_base16(get_data()) + "]";
48✔
473
}
474

475
// Properties.
476
// ----------------------------------------------------------------------------
477

478
bool operation::is_valid() const NOEXCEPT
10,800✔
479
{
480
    // Push data not possible with any is_invalid, combination is invalid.
481
    // This is necessary because there can be no invalid sentinel value.
482
    return !(code_ == any_invalid && !underflow_ && !data_empty());
10,800✔
483
}
484

485
opcode operation::code() const NOEXCEPT
1,227,401✔
486
{
487
    return code_;
1,227,401✔
488
}
489

490
const data_chunk& operation::data() const NOEXCEPT
354✔
491
{
492
    return get_data();
354✔
493
}
494

495
const chunk_cptr& operation::data_ptr() const NOEXCEPT
4,474✔
496
{
497
    return get_data_cptr();
4,474✔
498
}
499

500
size_t operation::serialized_size() const NOEXCEPT
11,232✔
501
{
502
    static constexpr auto op_size = sizeof(uint8_t);
11,232✔
503
    const auto size = data_size();
11,232✔
504

505
    if (underflow_)
11,232✔
506
        return size;
507

508
    switch (code_)
11,229✔
509
    {
510
        case opcode::push_one_size:
14✔
511
            return op_size + sizeof(uint8_t) + size;
14✔
512
        case opcode::push_two_size:
67✔
513
            return op_size + sizeof(uint16_t) + size;
67✔
514
        case opcode::push_four_size:
5✔
515
            return op_size + sizeof(uint32_t) + size;
5✔
516
        default:
11,143✔
517
            return op_size + size;
11,143✔
518
    }
519
}
520

521
// Utilities.
522
// ----------------------------------------------------------------------------
523

524
// static/private
525
// Advances stream, returns true unless exhausted.
526
// Does not advance to end position in the case of underflow operation.
527
bool operation::count_op(reader& source) NOEXCEPT
83,387✔
528
{
529
    if (source.is_exhausted())
83,387✔
530
        return false;
531

532
    const auto code = static_cast<opcode>(source.read_byte());
82,691✔
533
    source.skip_bytes(read_data_size(code, source));
82,691✔
534
    return true;
82,691✔
535
}
536

537
// static/private
538
uint32_t operation::read_data_size(opcode code, reader& source) NOEXCEPT
165,424✔
539
{
540
    constexpr auto op_75 = static_cast<uint8_t>(opcode::push_size_75);
165,424✔
541

542
    switch (code)
165,424✔
543
    {
544
        case opcode::push_one_size:
11✔
545
            return source.read_byte();
11✔
546
        case opcode::push_two_size:
9✔
547
            return source.read_2_bytes_little_endian();
9✔
548
        case opcode::push_four_size:
5✔
549
            return source.read_4_bytes_little_endian();
5✔
550
        default:
165,399✔
551
            const auto byte = static_cast<uint8_t>(code);
165,399✔
552
            return byte <= op_75 ? byte : 0;
165,399✔
553
    }
554
}
555

556
// Categories of operations.
557
// ----------------------------------------------------------------------------
558

559
bool operation::is_invalid() const NOEXCEPT
115,818✔
560
{
561
    return is_invalid(code_);
115,818✔
562
}
563

564
bool operation::is_push() const NOEXCEPT
1✔
565
{
566
    return is_push(code_);
1✔
567
}
568

569
bool operation::is_payload() const NOEXCEPT
×
570
{
571
    return is_payload(code_);
×
572
}
573

574
bool operation::is_counted() const NOEXCEPT
×
575
{
576
    return is_counted(code_);
×
577
}
578

579
bool operation::is_version() const NOEXCEPT
206✔
580
{
581
    return is_version(code_);
206✔
582
}
583

584
bool operation::is_numeric() const NOEXCEPT
×
585
{
586
    return is_numeric(code_);
×
587
}
588

589
bool operation::is_positive() const NOEXCEPT
×
590
{
591
    return is_positive(code_);
×
592
}
593

594
bool operation::is_reserved() const NOEXCEPT
×
595
{
596
    return is_reserved(code_);
×
597
}
598

599
bool operation::is_conditional() const NOEXCEPT
22,402✔
600
{
601
    return is_conditional(code_);
22,402✔
602
}
603

604
bool operation::is_relaxed_push() const NOEXCEPT
×
605
{
606
    return is_relaxed_push(code_);
×
607
}
608

609
bool operation::is_minimal_push() const NOEXCEPT
129✔
610
{
611
    return code_ == minimal_opcode_from_data(get_data());
129✔
612
}
613

614
bool operation::is_nominal_push() const NOEXCEPT
×
615
{
616
    return code_ == nominal_opcode_from_data(get_data());
×
617
}
618

619
bool operation::is_oversized() const NOEXCEPT
22,413✔
620
{
621
    // Rule max_push_data_size imposed by [0.3.6] soft fork.
622
    return data_size() > max_push_data_size;
22,413✔
623
}
624

625
bool operation::is_underclaimed() const NOEXCEPT
4,474✔
626
{
627
    return data_size() > operation::opcode_to_maximum_size(code_);
4,474✔
628
}
629

630
// ****************************************************************************
631
// CONSENSUS: An underflow is sized op-undersized data. This is valid as long
632
// as the operation is not executed. For example, coinbase input scripts.
633
// ****************************************************************************
634
bool operation::is_underflow() const NOEXCEPT
1,491,451✔
635
{
636
    return underflow_;
1,491,443✔
637
}
638

639
// JSON value convertors.
640
// ----------------------------------------------------------------------------
641

642
namespace json = boost::json;
643

644
// boost/json will soon have NOEXCEPT: github.com/boostorg/json/pull/636
645
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
646

647
operation tag_invoke(json::value_to_tag<operation>,
1✔
648
    const json::value& value) NOEXCEPT
649
{
650
    return operation{ std::string(value.get_string().c_str()) };
2✔
651
}
652

653
void tag_invoke(json::value_from_tag, json::value& value,
2✔
654
    const operation& operation) NOEXCEPT
655
{
656
    value = operation.to_string(flags::all_rules);
2✔
657
}
2✔
658

659
BC_POP_WARNING()
660

661
operation::cptr tag_invoke(json::value_to_tag<operation::cptr>,
×
662
    const json::value& value) NOEXCEPT
663
{
664
    return to_shared(tag_invoke(json::value_to_tag<operation>{}, value));
×
665
}
666

667
// Shared pointer overload is required for navigation.
668
BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
669
BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)
670

671
void tag_invoke(json::value_from_tag tag, json::value& value,
×
672
    const operation::cptr& operation) NOEXCEPT
673
{
674
    tag_invoke(tag, value, *operation);
×
675
}
×
676

677
BC_POP_WARNING()
678
BC_POP_WARNING()
679
BC_POP_WARNING()
680

681
} // namespace chain
682
} // namespace system
683
} // 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

© 2026 Coveralls, Inc