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

libbitcoin / libbitcoin-system / 10442590839

18 Aug 2024 05:46PM UTC coverage: 83.245% (-0.03%) from 83.273%
10442590839

Pull #1520

github

web-flow
Merge 831c7dc16 into 616e6358b
Pull Request #1520: Pass allocator to block shared_ptr constructions.

28 of 31 new or added lines in 9 files covered. (90.32%)

4 existing lines in 2 files now uncovered.

10056 of 12080 relevant lines covered (83.25%)

4780448.39 hits per line

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

87.59
/src/chain/operation.cpp
1

2
/**
3
 * Copyright (c) 2011-2023 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
162✔
47
{
48
    static const data_chunk empty_data{};
162✔
49
    return empty_data;
162✔
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 }
1✔
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,526✔
83
{
84
    return data_ ? data_->size() : zero;
1,529,526✔
85
}
86

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

92
const chunk_cptr& operation::get_data_cptr() const NOEXCEPT
4,470✔
93
{
94
    return data_ ? data_ : no_data_cptr();
4,470✔
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(const data_slice& op_data) NOEXCEPT
35✔
127
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
128
  : operation(stream::in::copy(op_data))
35✔
129
    BC_POP_WARNING()
130
{
131
}
35✔
132

133
////operation::operation(stream::in::fast&& stream) NOEXCEPT
134
////  : operation(read::bytes::fast(stream))
135
////{
136
////}
137

138
operation::operation(stream::in::fast& stream) NOEXCEPT
1✔
139
  : operation(read::bytes::fast(stream))
1✔
140
{
141
}
1✔
142

143
operation::operation(std::istream&& stream) NOEXCEPT
35✔
144
  : operation(read::bytes::istream(stream))
35✔
145
{
146
}
35✔
147

148
operation::operation(std::istream& stream) NOEXCEPT
4✔
149
  : operation(read::bytes::istream(stream))
4✔
150
{
151
}
4✔
152

153
operation::operation(reader&& source) NOEXCEPT
40✔
154
  : operation(source)
40✔
155
{
156
}
×
157

158
operation::operation(reader& source) NOEXCEPT
82,608✔
159
{
160
    assign_data(source);
82,608✔
161
}
82,608✔
162

163
operation::operation(const std::string& mnemonic) NOEXCEPT
10,729✔
164
  : operation(from_string(mnemonic))
10,729✔
165
{
166
}
10,729✔
167

168
// protected
169
operation::operation(opcode code, const chunk_cptr& push_data,
11,234✔
170
    bool underflow) NOEXCEPT
462✔
171
  : code_(code), data_(push_data), underflow_(underflow)
11,234✔
172
{
173
}
×
174

175
// Operators.
176
// ----------------------------------------------------------------------------
177

178
bool operation::operator==(const operation& other) const NOEXCEPT
194✔
179
{
180
    return (code_ == other.code_)
194✔
181
        && (data_ == other.data_ || get_data() == other.get_data())
184✔
182
        && (underflow_ == other.underflow_);
378✔
183
}
184

185
bool operation::operator!=(const operation& other) const NOEXCEPT
2✔
186
{
187
    return !(*this == other);
2✔
188
}
189

190
// Deserialization.
191
// ----------------------------------------------------------------------------
192

193
// private
194
void operation::assign_data(reader& source) NOEXCEPT
82,608✔
195
{
196
    auto& allocator = source.get_allocator();
82,608✔
197

198
    // Guard against resetting a previously-invalid stream.
199
    if (!source)
82,608✔
200
    {
NEW
201
        INPLACE(&data_, data_chunk, allocator, nullptr);
×
202
        return;
×
203
    }
204

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

215
    // Size of a push-data opcode is not retained, as this is inherent in data.
216
    code_ = static_cast<opcode>(source.read_byte());
82,608✔
217
    const auto size = read_data_size(code_, source);
82,608✔
218

219
    // read_bytes only guarded from excessive allocation by stream limit.
220
    if (size > max_block_size)
82,608✔
221
        source.invalidate();
1✔
222

223
    // An invalid source.read_bytes_raw returns nullptr.
224
    INPLACE(&data_, data_chunk, allocator, source.read_bytes_raw(size));
82,608✔
225
    underflow_ = !source;
82,608✔
226

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

240
    // All byte vectors are deserializable, stream indicates own failure.
241
}
242

243
// static/private
244
operation operation::from_push_data(const chunk_cptr& data,
49✔
245
    bool minimal) NOEXCEPT
246
{
247
    const auto code = opcode_from_data(*data, minimal);
49✔
248

249
    // Minimal interpretation affects only single byte push data.
250
    // Revert data if (minimal) opcode_from_data produced a numeric encoding.
251
    const auto push = is_payload(code) ? data : nullptr;
49✔
252

253
    return { code, push, false };
49✔
254
}
255

256
inline bool is_push_token(const std::string& token) NOEXCEPT
10,729✔
257
{
258
    return token.size() > one && token.front() == '[' && token.back() == ']';
10,729✔
259
}
260

261
inline bool is_text_token(const std::string& token) NOEXCEPT
10,572✔
262
{
263
    return token.size() > one && token.front() == '\'' && token.back() == '\'';
10,572✔
264
}
265

266
inline bool is_underflow_token(const std::string& token) NOEXCEPT
9,327✔
267
{
268
    return token.size() > one && token.front() == '<' && token.back() == '>';
9,327✔
269
}
270

271
inline std::string remove_token_delimiters(const std::string& token) NOEXCEPT
1,417✔
272
{
273
    BC_ASSERT(token.size() > one);
1,417✔
274
    return std::string(std::next(token.begin()), std::prev(token.end()));
1,417✔
275
}
276

277
inline string_list split_push_token(const std::string& token) NOEXCEPT
157✔
278
{
279
    return split(remove_token_delimiters(token), ".", false, false);
314✔
280
}
281

282
static bool opcode_from_data_prefix(opcode& out_code,
22✔
283
    const std::string& prefix, const data_chunk& push_data) NOEXCEPT
284
{
285
    constexpr auto op_75 = static_cast<uint8_t>(opcode::push_size_75);
22✔
286
    const auto size = push_data.size();
22✔
287
    out_code = operation::opcode_from_size(size);
22✔
288

289
    if (prefix == "0")
22✔
290
    {
291
        return size <= op_75;
2✔
292
    }
293
    else if (prefix == "1")
20✔
294
    {
295
        out_code = opcode::push_one_size;
10✔
296
        return size <= max_uint8;
10✔
297
    }
298
    else if (prefix == "2")
10✔
299
    {
300
        out_code = opcode::push_two_size;
5✔
301
        return size <= max_uint16;
5✔
302
    }
303
    else if (prefix == "4")
5✔
304
    {
305
        out_code = opcode::push_four_size;
3✔
306
        return size <= max_uint32;
3✔
307
    }
308

309
    return false;
310
}
311

312
static bool data_from_decimal(data_chunk& out_data,
296✔
313
    const std::string& token) NOEXCEPT
314
{
315
    // Deserialization to a number can convert random text to zero.
316
    if (!is_ascii_numeric(token))
296✔
317
        return false;
318

319
    int64_t value;
296✔
320
    if (!deserialize(value, token))
296✔
321
        return false;
322

323
    out_data = machine::number::chunk::from_integer(value);
296✔
324
    return true;
296✔
325
}
326

327
// private/static
328
operation operation::from_string(const std::string& mnemonic) NOEXCEPT
10,729✔
329
{
330
    data_chunk chunk;
10,729✔
331
    auto valid = false;
10,729✔
332
    auto underflow = false;
10,729✔
333

334
    // Always defined below, but this fixes warning.
335
    opcode code{ opcode::op_xor };
10,729✔
336

337
    if (is_push_token(mnemonic))
10,729✔
338
    {
339
        // Data encoding uses single token with one or two parts.
340
        const auto parts = split_push_token(mnemonic);
157✔
341

342
        if (parts.size() == one)
157✔
343
        {
344
            // Extract operation using nominal data size decoding.
345
            if ((valid = decode_base16(chunk, parts.front())))
132✔
346
                code = nominal_opcode_from_data(chunk);
132✔
347
        }
348
        else if (parts.size() == two)
25✔
349
        {
350
            // Extract operation using explicit data size decoding.
351

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

386
    if (!valid)
10,729✔
387
        return {};
6✔
388

389
    return { code, to_shared(std::move(chunk)), underflow };
21,446✔
390
}
391

392
// Serialization.
393
// ----------------------------------------------------------------------------
394

395
data_chunk operation::to_data() const NOEXCEPT
24✔
396
{
397
    data_chunk data(serialized_size());
24✔
398

399
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
400
    stream::out::copy ostream(data);
24✔
401
    BC_POP_WARNING()
402

403
    to_data(ostream);
24✔
404
    return data;
48✔
405
}
24✔
406

407
void operation::to_data(std::ostream& stream) const NOEXCEPT
25✔
408
{
409
    write::bytes::ostream out(stream);
25✔
410
    to_data(out);
25✔
411
}
25✔
412

413
void operation::to_data(writer& sink) const NOEXCEPT
1,491,452✔
414
{
415
    // Underflow is op-undersized data, it is serialized with no opcode.
416
    // An underflow could only be a final token in a script deserialization.
417
    if (is_underflow())
1,491,452✔
418
    {
419
        sink.write_bytes(get_data());
14✔
420
    }
421
    else
422
    {
423
        const auto size = data_size();
1,491,438✔
424
        sink.write_byte(static_cast<uint8_t>(code_));
1,491,438✔
425

426
        switch (code_)
1,491,438✔
427
        {
428
            case opcode::push_one_size:
2✔
429
                sink.write_byte(narrow_cast<uint8_t>(size));
2✔
430
                break;
2✔
431
            case opcode::push_two_size:
3✔
432
                sink.write_2_bytes_little_endian(narrow_cast<uint16_t>(size));
3✔
433
                break;
3✔
434
            case opcode::push_four_size:
2✔
435
                sink.write_4_bytes_little_endian(
2✔
436
                    possible_narrow_cast<uint32_t>(size));
437
                break;
2✔
438
            default:
439
            break;
440
        }
441

442
        sink.write_bytes(get_data());
1,491,438✔
443
    }
444
}
1,491,452✔
445

446
// To String.
447
// ----------------------------------------------------------------------------
448

449
static std::string opcode_to_prefix(opcode code,
16✔
450
    const data_chunk& data) NOEXCEPT
451
{
452
    // If opcode is minimal for a size-based encoding, do not set a prefix.
453
    if (code == operation::opcode_from_size(data.size()))
16✔
454
        return "";
13✔
455

456
    switch (code)
3✔
457
    {
458
        case opcode::push_one_size:
1✔
459
            return "1.";
1✔
460
        case opcode::push_two_size:
1✔
461
            return "2.";
1✔
462
        case opcode::push_four_size:
1✔
463
            return "4.";
1✔
464
        default:
×
465
            return "0.";
×
466
    }
467
}
468

469
std::string operation::to_string(uint32_t active_flags) const NOEXCEPT
77✔
470
{
471
    if (!is_valid())
77✔
472
        return "(?)";
×
473

474
    if (underflow_)
77✔
475
        return "<" + encode_base16(get_data()) + ">";
4✔
476

477
    if (data_empty())
75✔
478
        return opcode_to_mnemonic(code_, active_flags);
59✔
479

480
    // Data encoding uses single token with explicit size prefix as required.
481
    return "[" + opcode_to_prefix(code_, get_data()) +
32✔
482
        encode_base16(get_data()) + "]";
48✔
483
}
484

485
// Properties.
486
// ----------------------------------------------------------------------------
487

488
bool operation::is_valid() const NOEXCEPT
10,791✔
489
{
490
    // Push data not possible with any is_invalid, combination is invalid.
491
    // This is necessary because there can be no invalid sentinel value.
492
    return !(code_ == any_invalid && !underflow_ && !data_empty());
10,791✔
493
}
494

495
opcode operation::code() const NOEXCEPT
1,227,365✔
496
{
497
    return code_;
1,227,365✔
498
}
499

500
const data_chunk& operation::data() const NOEXCEPT
378✔
501
{
502
    return get_data();
378✔
503
}
504

505
const chunk_cptr& operation::data_ptr() const NOEXCEPT
4,470✔
506
{
507
    return get_data_cptr();
4,470✔
508
}
509

510
size_t operation::serialized_size() const NOEXCEPT
11,223✔
511
{
512
    static constexpr auto op_size = sizeof(uint8_t);
11,223✔
513
    const auto size = data_size();
11,223✔
514

515
    if (underflow_)
11,223✔
516
        return size;
517

518
    switch (code_)
11,220✔
519
    {
520
        case opcode::push_one_size:
13✔
521
            return op_size + sizeof(uint8_t) + size;
13✔
522
        case opcode::push_two_size:
67✔
523
            return op_size + sizeof(uint16_t) + size;
67✔
524
        case opcode::push_four_size:
5✔
525
            return op_size + sizeof(uint32_t) + size;
5✔
526
        default:
11,135✔
527
            return op_size + size;
11,135✔
528
    }
529
}
530

531
// Utilities.
532
// ----------------------------------------------------------------------------
533

534
// static/private
535
// Advances stream, returns true unless exhausted.
536
// Does not advance to end position in the case of underflow operation.
537
bool operation::count_op(reader& source) NOEXCEPT
83,212✔
538
{
539
    if (source.is_exhausted())
83,212✔
540
        return false;
541

542
    const auto code = static_cast<opcode>(source.read_byte());
82,566✔
543
    source.skip_bytes(read_data_size(code, source));
82,566✔
544
    return true;
82,566✔
545
}
546

547
// static/private
548
uint32_t operation::read_data_size(opcode code, reader& source) NOEXCEPT
165,174✔
549
{
550
    constexpr auto op_75 = static_cast<uint8_t>(opcode::push_size_75);
165,174✔
551

552
    switch (code)
165,174✔
553
    {
554
        case opcode::push_one_size:
11✔
555
            return source.read_byte();
11✔
556
        case opcode::push_two_size:
9✔
557
            return source.read_2_bytes_little_endian();
9✔
558
        case opcode::push_four_size:
5✔
559
            return source.read_4_bytes_little_endian();
5✔
560
        default:
165,149✔
561
            const auto byte = static_cast<uint8_t>(code);
165,149✔
562
            return byte <= op_75 ? byte : 0;
165,149✔
563
    }
564
}
565

566
// Categories of operations.
567
// ----------------------------------------------------------------------------
568

569
bool operation::is_invalid() const NOEXCEPT
115,666✔
570
{
571
    return is_invalid(code_);
115,666✔
572
}
573

574
bool operation::is_push() const NOEXCEPT
1✔
575
{
576
    return is_push(code_);
1✔
577
}
578

579
bool operation::is_payload() const NOEXCEPT
×
580
{
581
    return is_payload(code_);
×
582
}
583

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

589
bool operation::is_version() const NOEXCEPT
205✔
590
{
591
    return is_version(code_);
205✔
592
}
593

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

599
bool operation::is_positive() const NOEXCEPT
×
600
{
601
    return is_positive(code_);
×
602
}
603

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

609
bool operation::is_conditional() const NOEXCEPT
22,384✔
610
{
611
    return is_conditional(code_);
22,384✔
612
}
613

614
bool operation::is_relaxed_push() const NOEXCEPT
×
615
{
616
    return is_relaxed_push(code_);
×
617
}
618

619
bool operation::is_minimal_push() const NOEXCEPT
8✔
620
{
621
    return code_ == minimal_opcode_from_data(get_data());
8✔
622
}
623

624
bool operation::is_nominal_push() const NOEXCEPT
×
625
{
626
    return code_ == nominal_opcode_from_data(get_data());
×
627
}
628

629
bool operation::is_oversized() const NOEXCEPT
22,395✔
630
{
631
    // Rule max_push_data_size imposed by [0.3.6] soft fork.
632
    return data_size() > max_push_data_size;
22,395✔
633
}
634

635
bool operation::is_underclaimed() const NOEXCEPT
4,470✔
636
{
637
    return data_size() > operation::opcode_to_maximum_size(code_);
4,470✔
638
}
639

640
// ****************************************************************************
641
// CONSENSUS: An underflow is sized op-undersized data. This is valid as long
642
// as the operation is not executed. For example, coinbase input scripts.
643
// ****************************************************************************
644
bool operation::is_underflow() const NOEXCEPT
1,491,460✔
645
{
646
    return underflow_;
1,491,452✔
647
}
648

649
// JSON value convertors.
650
// ----------------------------------------------------------------------------
651

652
namespace json = boost::json;
653

654
// boost/json will soon have NOEXCEPT: github.com/boostorg/json/pull/636
655
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
656

657
operation tag_invoke(json::value_to_tag<operation>,
1✔
658
    const json::value& value) NOEXCEPT
659
{
660
    return operation{ std::string(value.get_string().c_str()) };
1✔
661
}
662

663
void tag_invoke(json::value_from_tag, json::value& value,
2✔
664
    const operation& operation) NOEXCEPT
665
{
666
    value = operation.to_string(flags::all_rules);
2✔
667
}
2✔
668

669
BC_POP_WARNING()
670

671
operation::cptr tag_invoke(json::value_to_tag<operation::cptr>,
×
672
    const json::value& value) NOEXCEPT
673
{
674
    return to_shared(tag_invoke(json::value_to_tag<operation>{}, value));
×
675
}
676

677
// Shared pointer overload is required for navigation.
678
BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
679
BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)
680

681
void tag_invoke(json::value_from_tag tag, json::value& value,
×
682
    const operation::cptr& operation) NOEXCEPT
683
{
684
    tag_invoke(tag, value, *operation);
×
685
}
×
686

687
BC_POP_WARNING()
688
BC_POP_WARNING()
689
BC_POP_WARNING()
690

691
} // namespace chain
692
} // namespace system
693
} // 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