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

libbitcoin / libbitcoin-system / 14917173188

08 May 2025 10:15PM UTC coverage: 82.739% (+0.1%) from 82.628%
14917173188

push

github

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

Fix machine|chain include cycles, refactor opcode utils.

120 of 147 new or added lines in 8 files covered. (81.63%)

1 existing line in 1 file now uncovered.

10287 of 12433 relevant lines covered (82.74%)

3875327.57 hits per line

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

87.73
/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_verif;
41
static_assert(operation::is_invalid(any_invalid));
42

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

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

53
// static/private
54
const chunk_cptr& operation::no_data_cptr() NOEXCEPT
×
55
{
56
    static const auto empty = std::make_shared<const data_chunk>();
×
57
    return empty;
×
58
}
59

60
// static/private
61
// Push data is not possible with an invalid code, combination is invalid.
62
const chunk_cptr& operation::any_data_cptr() NOEXCEPT
10✔
63
{
64
    static const auto any = to_shared(data_chunk{ 0x42 });
12✔
65
    return any;
10✔
66
}
67

68
bool operation::data_empty() const NOEXCEPT
84✔
69
{
70
    return !data_ || data_->empty();
84✔
71
}
72

73
size_t operation::data_size() const NOEXCEPT
1,529,548✔
74
{
75
    return data_ ? data_->size() : zero;
1,529,548✔
76
}
77

78
const data_chunk& operation::get_data() const NOEXCEPT
1,492,376✔
79
{
80
    return data_ ? *data_ : no_data();
1,492,376✔
81
}
82

83
const chunk_cptr& operation::get_data_cptr() const NOEXCEPT
4,474✔
84
{
85
    return data_ ? data_ : no_data_cptr();
4,474✔
86
}
87

88
// Constructors.
89
// ----------------------------------------------------------------------------
90

91
operation::operation() NOEXCEPT
10✔
92
  : operation(any_invalid, any_data_cptr(), false)
10✔
93
{
94
}
10✔
95

96
// If code is push data the data member will be inconsistent (empty).
97
operation::operation(opcode code) NOEXCEPT
451✔
98
  : operation(code, nullptr, false)
451✔
99
{
100
}
451✔
101

102
operation::operation(data_chunk&& push_data, bool minimal) NOEXCEPT
44✔
103
  : operation(from_push_data(to_shared(std::move(push_data)), minimal))
44✔
104
{
105
}
44✔
106

107
operation::operation(const data_chunk& push_data, bool minimal) NOEXCEPT
5✔
108
  : operation(from_push_data(to_shared(push_data), minimal))
5✔
109
{
110
}
5✔
111

112
operation::operation(const chunk_cptr& push_data, bool minimal) NOEXCEPT
×
113
  : operation(from_push_data(push_data, minimal))
×
114
{
115
}
×
116

117
operation::operation(stream::in::fast&& stream) NOEXCEPT
35✔
118
  : operation(read::bytes::fast(stream))
35✔
119
{
120
}
35✔
121

122
operation::operation(stream::in::fast& stream) NOEXCEPT
1✔
123
  : operation(read::bytes::fast(stream))
1✔
124
{
125
}
1✔
126

127
operation::operation(std::istream&& stream) NOEXCEPT
×
128
  : operation(read::bytes::istream(stream))
×
129
{
130
}
×
131

132
operation::operation(std::istream& stream) NOEXCEPT
4✔
133
  : operation(read::bytes::istream(stream))
4✔
134
{
135
}
4✔
136

137
operation::operation(reader&& source) NOEXCEPT
40✔
138
  : operation(source)
40✔
139
{
140
}
×
141

142
operation::operation(reader& source) NOEXCEPT
82,733✔
143
{
144
    assign_data(source);
82,733✔
145
}
82,733✔
146

147
operation::operation(const std::string& mnemonic) NOEXCEPT
10,738✔
148
  : operation(from_string(mnemonic))
10,738✔
149
{
150
}
10,738✔
151

152
// protected
153
operation::operation(opcode code, const chunk_cptr& push_data,
11,242✔
154
    bool underflow) NOEXCEPT
461✔
155
  : code_(code), data_(push_data), underflow_(underflow)
11,242✔
156
{
157
}
×
158

159
// Operators.
160
// ----------------------------------------------------------------------------
161

162
bool operation::operator==(const operation& other) const NOEXCEPT
236✔
163
{
164
    return (code_ == other.code_)
236✔
165
        && (data_ == other.data_ || get_data() == other.get_data())
234✔
166
        && (underflow_ == other.underflow_);
470✔
167
}
168

169
bool operation::operator!=(const operation& other) const NOEXCEPT
2✔
170
{
171
    return !(*this == other);
2✔
172
}
173

174
// Deserialization.
175
// ----------------------------------------------------------------------------
176

177
// private
178
void operation::assign_data(reader& source) NOEXCEPT
82,733✔
179
{
180
    auto& allocator = source.get_allocator();
82,733✔
181

182
    // Guard against resetting a previously-invalid stream.
183
    if (!source)
82,733✔
184
    {
185
        INPLACE(&data_, data_chunk, allocator, nullptr);
×
186
        return;
82,722✔
187
    }
188

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

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

203
    // read_bytes only guarded from excessive allocation by stream limit.
204
    if (size > max_block_size)
82,733✔
205
        source.invalidate();
1✔
206

207
    // An invalid source.read_bytes_raw returns nullptr.
208
    const auto ptr = source.read_bytes_raw(size);
82,733✔
209
    underflow_ = !source;
82,733✔
210
    if (!underflow_)
82,733✔
211
    {
212
        INPLACE(&data_, data_chunk, allocator, ptr);
82,722✔
213
        return;
82,722✔
214
    }
215

216
    // This requires that provided stream terminates at the end of the script.
217
    // When passing ops as part of a stream longer than the script, such as for
218
    // a transaction, caller should apply source.set_limit(prefix_size), and
219
    // clear the stream limit upon return. Stream invalidation and set_position
220
    // do not alter a stream limit, it just behaves as a smaller stream buffer.
221
    // Without a limit, source.read_bytes() below consumes the remaining stream.
222
    code_ = any_invalid;
11✔
223
    source.set_position(start);
11✔
224
    INPLACE(&data_, data_chunk, allocator, source.read_bytes_raw());
11✔
225
}
226

227
// static/private
228
operation operation::from_push_data(const chunk_cptr& data,
49✔
229
    bool minimal) NOEXCEPT
230
{
231
    const auto code = opcode_from_data(*data, minimal);
49✔
232

233
    // Minimal interpretation affects only single byte push data.
234
    // Revert data if (minimal) opcode_from_data produced a numeric encoding.
235
    const auto push = is_payload(code) ? data : nullptr;
49✔
236

237
    return { code, push, false };
49✔
238
}
239

240
inline bool is_push_token(const std::string& token) NOEXCEPT
10,738✔
241
{
242
    return token.size() > one && token.front() == '[' && token.back() == ']';
10,738✔
243
}
244

245
inline bool is_text_token(const std::string& token) NOEXCEPT
10,579✔
246
{
247
    return token.size() > one && token.front() == '\'' && token.back() == '\'';
10,579✔
248
}
249

250
inline bool is_underflow_token(const std::string& token) NOEXCEPT
9,334✔
251
{
252
    return token.size() > one && token.front() == '<' && token.back() == '>';
9,334✔
253
}
254

255
inline std::string remove_token_delimiters(const std::string& token) NOEXCEPT
1,419✔
256
{
257
    BC_ASSERT(token.size() > one);
1,419✔
258
    return std::string(std::next(token.begin()), std::prev(token.end()));
1,419✔
259
}
260

261
inline string_list split_push_token(const std::string& token) NOEXCEPT
159✔
262
{
263
    return split(remove_token_delimiters(token), ".", false, false);
318✔
264
}
265

266
static bool opcode_from_data_prefix(opcode& out_code,
23✔
267
    const std::string& prefix, const data_chunk& push_data) NOEXCEPT
268
{
269
    constexpr auto op_75 = static_cast<uint8_t>(opcode::push_size_75);
23✔
270
    const auto size = push_data.size();
23✔
271
    out_code = operation::opcode_from_size(size);
23✔
272

273
    if (prefix == "0")
23✔
274
    {
275
        return size <= op_75;
2✔
276
    }
277
    else if (prefix == "1")
21✔
278
    {
279
        out_code = opcode::push_one_size;
11✔
280
        return size <= max_uint8;
11✔
281
    }
282
    else if (prefix == "2")
10✔
283
    {
284
        out_code = opcode::push_two_size;
5✔
285
        return size <= max_uint16;
5✔
286
    }
287
    else if (prefix == "4")
5✔
288
    {
289
        out_code = opcode::push_four_size;
3✔
290
        return size <= max_uint32;
3✔
291
    }
292

293
    return false;
294
}
295

296
static bool data_from_decimal(data_chunk& out_data,
296✔
297
    const std::string& token) NOEXCEPT
298
{
299
    // Deserialization to a number can convert random text to zero.
300
    if (!is_ascii_numeric(token))
296✔
301
        return false;
302

303
    int64_t value;
296✔
304
    if (!deserialize(value, token))
296✔
305
        return false;
306

307
    out_data = machine::number::chunk::from_integer(value);
296✔
308
    return true;
296✔
309
}
310

311
// private/static
312
operation operation::from_string(const std::string& mnemonic) NOEXCEPT
10,738✔
313
{
314
    data_chunk chunk;
10,738✔
315
    auto valid = false;
10,738✔
316
    auto underflow = false;
10,738✔
317

318
    // Always defined below, but this fixes warning.
319
    opcode code{ any_invalid };
10,738✔
320

321
    if (is_push_token(mnemonic))
10,738✔
322
    {
323
        // Data encoding uses single token with one or two parts.
324
        const auto parts = split_push_token(mnemonic);
159✔
325

326
        if (parts.size() == one)
159✔
327
        {
328
            // Extract operation using nominal data size decoding.
329
            if ((valid = decode_base16(chunk, parts.front())))
133✔
330
                code = nominal_opcode_from_data(chunk);
133✔
331
        }
332
        else if (parts.size() == two)
26✔
333
        {
334
            // Extract operation using explicit data size decoding.
335

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

370
    if (!valid)
10,738✔
371
        return {};
6✔
372

373
    return { code, to_shared(std::move(chunk)), underflow };
21,464✔
374
}
375

376
// Serialization.
377
// ----------------------------------------------------------------------------
378

379
data_chunk operation::to_data() const NOEXCEPT
24✔
380
{
381
    data_chunk data(serialized_size());
24✔
382
    stream::out::fast ostream(data);
24✔
383
    write::bytes::fast out(ostream);
24✔
384
    to_data(out);
24✔
385
    return data;
48✔
386
}
24✔
387

388
void operation::to_data(std::ostream& stream) const NOEXCEPT
1✔
389
{
390
    write::bytes::ostream out(stream);
1✔
391
    to_data(out);
1✔
392
}
1✔
393

394
void operation::to_data(writer& sink) const NOEXCEPT
1,491,443✔
395
{
396
    // Underflow is op-undersized data, it is serialized with no opcode.
397
    // An underflow could only be a final token in a script deserialization.
398
    if (is_underflow())
1,491,443✔
399
    {
400
        sink.write_bytes(get_data());
14✔
401
    }
402
    else
403
    {
404
        const auto size = data_size();
1,491,429✔
405
        sink.write_byte(static_cast<uint8_t>(code_));
1,491,429✔
406

407
        switch (code_)
1,491,429✔
408
        {
409
            case opcode::push_one_size:
2✔
410
                sink.write_byte(narrow_cast<uint8_t>(size));
2✔
411
                break;
2✔
412
            case opcode::push_two_size:
3✔
413
                sink.write_2_bytes_little_endian(narrow_cast<uint16_t>(size));
3✔
414
                break;
3✔
415
            case opcode::push_four_size:
2✔
416
                sink.write_4_bytes_little_endian(
2✔
417
                    possible_narrow_cast<uint32_t>(size));
418
                break;
2✔
419
            default:
420
            break;
421
        }
422

423
        sink.write_bytes(get_data());
1,491,429✔
424
    }
425
}
1,491,443✔
426

427
// To String.
428
// ----------------------------------------------------------------------------
429

430
static std::string opcode_to_prefix(opcode code,
16✔
431
    const data_chunk& data) NOEXCEPT
432
{
433
    // If opcode is minimal for a size-based encoding, do not set a prefix.
434
    if (code == operation::opcode_from_size(data.size()))
16✔
435
        return "";
13✔
436

437
    switch (code)
3✔
438
    {
439
        case opcode::push_one_size:
1✔
440
            return "1.";
1✔
441
        case opcode::push_two_size:
1✔
442
            return "2.";
1✔
443
        case opcode::push_four_size:
1✔
444
            return "4.";
1✔
445
        default:
×
446
            return "0.";
×
447
    }
448
}
449

450
std::string operation::to_string(uint32_t active_flags) const NOEXCEPT
77✔
451
{
452
    if (!is_valid())
77✔
453
        return "(?)";
×
454

455
    if (underflow_)
77✔
456
        return "<" + encode_base16(get_data()) + ">";
4✔
457

458
    if (data_empty())
75✔
459
        return opcode_to_mnemonic(code_, active_flags);
59✔
460

461
    // Data encoding uses single token with explicit size prefix as required.
462
    return "[" + opcode_to_prefix(code_, get_data()) +
32✔
463
        encode_base16(get_data()) + "]";
48✔
464
}
465

466
// Properties.
467
// ----------------------------------------------------------------------------
468

469
bool operation::is_valid() const NOEXCEPT
10,800✔
470
{
471
    // Push data not possible with any is_invalid, combination is invalid.
472
    // This is necessary because there can be no invalid sentinel value.
473
    return !(code_ == any_invalid && !underflow_ && !data_empty());
10,800✔
474
}
475

476
opcode operation::code() const NOEXCEPT
1,227,401✔
477
{
478
    return code_;
1,227,401✔
479
}
480

481
const data_chunk& operation::data() const NOEXCEPT
354✔
482
{
483
    return get_data();
354✔
484
}
485

486
const chunk_cptr& operation::data_ptr() const NOEXCEPT
4,474✔
487
{
488
    return get_data_cptr();
4,474✔
489
}
490

491
size_t operation::serialized_size() const NOEXCEPT
11,232✔
492
{
493
    static constexpr auto op_size = sizeof(uint8_t);
11,232✔
494
    const auto size = data_size();
11,232✔
495

496
    if (underflow_)
11,232✔
497
        return size;
498

499
    switch (code_)
11,229✔
500
    {
501
        case opcode::push_one_size:
14✔
502
            return op_size + sizeof(uint8_t) + size;
14✔
503
        case opcode::push_two_size:
67✔
504
            return op_size + sizeof(uint16_t) + size;
67✔
505
        case opcode::push_four_size:
5✔
506
            return op_size + sizeof(uint32_t) + size;
5✔
507
        default:
11,143✔
508
            return op_size + size;
11,143✔
509
    }
510
}
511

512
// Utilities.
513
// ----------------------------------------------------------------------------
514

515
// static/private
516
// Advances stream, returns true unless exhausted.
517
// Does not advance to end position in the case of underflow operation.
518
bool operation::count_op(reader& source) NOEXCEPT
83,387✔
519
{
520
    if (source.is_exhausted())
83,387✔
521
        return false;
522

523
    const auto code = static_cast<opcode>(source.read_byte());
82,691✔
524
    source.skip_bytes(read_data_size(code, source));
82,691✔
525
    return true;
82,691✔
526
}
527

528
// static/private
529
uint32_t operation::read_data_size(opcode code, reader& source) NOEXCEPT
165,424✔
530
{
531
    constexpr auto op_75 = static_cast<uint8_t>(opcode::push_size_75);
165,424✔
532

533
    switch (code)
165,424✔
534
    {
535
        case opcode::push_one_size:
11✔
536
            return source.read_byte();
11✔
537
        case opcode::push_two_size:
9✔
538
            return source.read_2_bytes_little_endian();
9✔
539
        case opcode::push_four_size:
5✔
540
            return source.read_4_bytes_little_endian();
5✔
541
        default:
165,399✔
542
            const auto byte = static_cast<uint8_t>(code);
165,399✔
543
            return byte <= op_75 ? byte : 0;
165,399✔
544
    }
545
}
546

547
// Categories of operations.
548
// ----------------------------------------------------------------------------
549

NEW
550
bool operation::is_relaxed_push() const NOEXCEPT
×
551
{
NEW
552
    return is_relaxed_push(code_);
×
553
}
554

555
bool operation::is_push() const NOEXCEPT
1✔
556
{
557
    return is_push(code_);
1✔
558
}
559

560
bool operation::is_payload() const NOEXCEPT
×
561
{
562
    return is_payload(code_);
×
563
}
564

NEW
565
bool operation::is_positive() const NOEXCEPT
×
566
{
NEW
567
    return is_positive(code_);
×
568
}
569

570
bool operation::is_nonnegative() const NOEXCEPT
206✔
571
{
572
    return is_nonnegative(code_);
206✔
573
}
574

NEW
575
bool operation::is_number() const NOEXCEPT
×
576
{
NEW
577
    return is_number(code_);
×
578
}
579

580
bool operation::is_roller() const NOEXCEPT
93,881✔
581
{
582
    return is_roller(code_);
93,881✔
583
}
584

NEW
585
bool operation::is_counted() const NOEXCEPT
×
586
{
NEW
587
    return is_counted(code_);
×
588
}
589

590
bool operation::is_success() const NOEXCEPT
93,881✔
591
{
592
    return is_success(code_);
93,881✔
593
}
594

595
bool operation::is_invalid() const NOEXCEPT
93,893✔
596
{
597
    return is_invalid(code_);
93,893✔
598
}
599

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

NEW
605
bool operation::is_reserved() const NOEXCEPT
×
606
{
NEW
607
    return is_reserved(code_);
×
608
}
609

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

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

620
bool operation::is_underclaimed() const NOEXCEPT
4,474✔
621
{
622
    return data_size() > operation::opcode_to_maximum_size(code_);
4,474✔
623
}
624

625
bool operation::is_oversized() const NOEXCEPT
22,413✔
626
{
627
    // Rule max_push_data_size imposed by [0.3.6] soft fork.
628
    return data_size() > max_push_data_size;
22,413✔
629
}
630

631
// ****************************************************************************
632
// CONSENSUS: An underflow is sized op-undersized data. Behavior is the same as
633
// invalid opcode, invalidating the script if executed and not success coded.
634
// ****************************************************************************
635
bool operation::is_underflow() const NOEXCEPT
1,491,511✔
636
{
637
    return underflow_;
1,491,443✔
638
}
639

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

643
namespace json = boost::json;
644

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

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

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

660
BC_POP_WARNING()
661

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

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

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

678
BC_POP_WARNING()
679
BC_POP_WARNING()
680
BC_POP_WARNING()
681

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