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

libbitcoin / libbitcoin-system / 9949774965

16 Jul 2024 02:30AM UTC coverage: 83.203% (+0.3%) from 82.874%
9949774965

Pull #1498

github

web-flow
Merge a266a0344 into 155e5fae6
Pull Request #1498: Optimizing deserializations.

205 of 222 new or added lines in 12 files covered. (92.34%)

14 existing lines in 8 files now uncovered.

10090 of 12127 relevant lines covered (83.2%)

4761709.16 hits per line

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

83.4
/src/chain/script.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/system/chain/script.hpp>
20

21
#include <algorithm>
22
#include <iterator>
23
#include <memory>
24
#include <numeric>
25
#include <sstream>
26
#include <utility>
27
#include <bitcoin/system/chain/enums/coverage.hpp>
28
#include <bitcoin/system/chain/enums/flags.hpp>
29
#include <bitcoin/system/chain/enums/script_pattern.hpp>
30
#include <bitcoin/system/chain/enums/script_version.hpp>
31
#include <bitcoin/system/chain/enums/magic_numbers.hpp>
32
#include <bitcoin/system/chain/enums/opcode.hpp>
33
#include <bitcoin/system/chain/operation.hpp>
34
#include <bitcoin/system/chain/transaction.hpp>
35
#include <bitcoin/system/chain/witness.hpp>
36
#include <bitcoin/system/data/data.hpp>
37
#include <bitcoin/system/define.hpp>
38
#include <bitcoin/system/error/error.hpp>
39
#include <bitcoin/system/hash/hash.hpp>
40
#include <bitcoin/system/machine/machine.hpp>
41
#include <bitcoin/system/radix/radix.hpp>
42
#include <bitcoin/system/stream/stream.hpp>
43

44
namespace libbitcoin {
45
namespace system {
46
namespace chain {
47

48
using namespace bc::system::machine;
49

50
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
51

52
// static
53
// TODO: would be inlined but machine is a circular include.
54
//*****************************************************************************
55
// CONSENSUS: BIP34 requires coinbase input script to begin with one byte
56
// that indicates height size. This is inconsistent with an extreme future
57
// where the size byte overflows. However satoshi actually requires nominal
58
// encoding.
59
//*************************************************************************
60
bool script::is_coinbase_pattern(const operations& ops, size_t height) NOEXCEPT
×
61
{
62
    BC_PUSH_WARNING(NO_ARRAY_INDEXING)
63
    return !ops.empty()
×
64
        && ops[0].is_nominal_push()
×
65
        && ops[0].data() == number::chunk::from_integer(to_unsigned(height));
×
66
    BC_POP_WARNING()
67
}
68

69
// Constructors.
70
// ----------------------------------------------------------------------------
71

72
script::script() NOEXCEPT
130✔
73
  : script(operations{}, false, false, zero)
260✔
74
{
75
}
130✔
76

77
script::~script() NOEXCEPT
4,336✔
78
{
79
}
4,336✔
80

81
script::script(script&& other) NOEXCEPT
370✔
82
  : script(std::move(other.ops_), other.valid_, other.prefail_, other.size_)
370✔
83
{
84
}
370✔
85

86
script::script(const script& other) NOEXCEPT
1,470✔
87
  : script(other.ops_, other.valid_, other.prefail_, other.size_)
1,470✔
88
{
89
}
1,470✔
90

91
// Prefail is false.
92
script::script(operations&& ops) NOEXCEPT
225✔
93
  : script(std::move(ops), true, false)
225✔
94
{
95
    // ops moved so cannot pass serialized_size(ops), order not guaranteed.
96
}
225✔
97

98
// Prefail is false.
99
script::script(const operations& ops) NOEXCEPT
1✔
100
  : script(ops, true, false, serialized_size(ops))
1✔
101
{
102
}
1✔
103

104
script::script(operations&& ops, bool prefail) NOEXCEPT
1,494✔
105
  : script(std::move(ops), true, prefail)
1,494✔
106
{
107
    // ops moved so cannot pass serialized_size(ops), order not guaranteed.
108
}
1,494✔
109

110
script::script(const operations& ops, bool prefail) NOEXCEPT
×
111
  : script(ops, true, prefail, serialized_size(ops))
×
112
{
113
}
×
114

115
script::script(const data_slice& data, bool prefix) NOEXCEPT
133✔
116
  : script(stream::in::copy(data), prefix)
133✔
117
{
118
}
133✔
119

120
////script::script(stream::in::fast&& stream, bool prefix) NOEXCEPT
121
////  : script(read::bytes::fast(stream), prefix)
122
////{
123
////}
124

125
script::script(stream::in::fast& stream, bool prefix) NOEXCEPT
1✔
126
  : script(read::bytes::fast(stream), prefix)
1✔
127
{
128
}
1✔
129

130
script::script(std::istream&& stream, bool prefix) NOEXCEPT
133✔
131
  : script(read::bytes::istream(stream), prefix)
133✔
132
{
133
}
133✔
134

135
script::script(std::istream& stream, bool prefix) NOEXCEPT
1✔
136
  : script(read::bytes::istream(stream), prefix)
1✔
137
{
138
}
1✔
139

140
script::script(reader&& source, bool prefix) NOEXCEPT
135✔
141
  : script(source, prefix/*from_data(source, prefix)*/)
135✔
142
{
UNCOV
143
}
×
144

145
script::script(reader& source, bool prefix) NOEXCEPT
646✔
146
////: script(from_data(source, prefix))
147
  : ops_(source.get_arena())
646✔
148
{
149
    assign_data(source, prefix);
646✔
150
}
646✔
151

152
script::script(const std::string& mnemonic) NOEXCEPT
1,498✔
153
  : script(from_string(mnemonic))
1,498✔
154
{
155
}
1,498✔
156

157
// protected
158
script::script(operations&& ops, bool valid, bool prefail) NOEXCEPT
1,719✔
159
  : ops_(std::move(ops)),
1,719✔
160
    valid_(valid),
1,719✔
161
    prefail_(prefail),
1,719✔
162
    size_(serialized_size(ops_)),
1,719✔
163
    offset(ops_.begin())
1,719✔
164
{
165
}
1,719✔
166

167
// protected
168
script::script(const operations& ops, bool valid, bool prefail) NOEXCEPT
×
169
  : ops_(ops),
×
170
    valid_(valid),
×
171
    prefail_(prefail),
×
172
    size_(serialized_size(ops)),
×
173
    offset(ops_.begin())
×
174
{
175
}
×
176

177
// protected
178
script::script(const operations& ops, bool valid, bool prefail,
1,971✔
179
    size_t size) NOEXCEPT
1,971✔
180
  : ops_(ops),
1,971✔
181
    valid_(valid),
1,971✔
182
    prefail_(prefail),
1,971✔
183
    size_(size),
130✔
184
    offset(ops_.begin())
1,971✔
185
{
186
}
×
187

188
// Operators.
189
// ----------------------------------------------------------------------------
190

191
script& script::operator=(script&& other) NOEXCEPT
4✔
192
{
193
    ops_ = std::move(other.ops_);
4✔
194
    valid_ = other.valid_;
4✔
195
    prefail_ = other.prefail_;
4✔
196
    size_ = other.size_;
4✔
197
    offset = ops_.begin();
4✔
198
    return *this;
4✔
199
}
200

201
script& script::operator=(const script& other) NOEXCEPT
×
202
{
203
    ops_ = other.ops_;
×
204
    valid_ = other.valid_;
×
205
    prefail_ = other.prefail_;
×
206
    size_ = other.size_;
×
207
    offset = ops_.begin();
×
208
    return *this;
×
209
}
210

211
bool script::operator==(const script& other) const NOEXCEPT
67✔
212
{
213
    return size_ == other.size_
67✔
214
        && ops_ == other.ops_;
67✔
215
}
216

217
bool script::operator!=(const script& other) const NOEXCEPT
×
218
{
219
    return !(*this == other);
×
220
}
221

222
// Deserialization.
223
// ----------------------------------------------------------------------------
224

225
// static/private
226
size_t script::op_count(reader& source) NOEXCEPT
646✔
227
{
228
    // Stream errors reset by set_position so trap here.
229
    if (!source)
646✔
230
        return zero;
231

232
    const auto start = source.get_read_position();
646✔
233
    auto count = zero;
646✔
234

235
    // This is expensive (1.1%) but far less than vector reallocs (11.6%).
236
    while (operation::count_op(source))
83,858✔
237
        ++count;
82,566✔
238

239
    source.set_position(start);
646✔
240
    return count;
646✔
241
}
242

243
// private
244
void script::assign_data(reader& source, bool prefix) NOEXCEPT
646✔
245
{
246
    size_t expected{};
646✔
247
    prefail_ = false;
646✔
248

249
    if (prefix)
646✔
250
    {
251
        expected = source.read_size();
517✔
252
        source.set_limit(expected);
517✔
253
    }
254

255
    ////auto& allocator = source.get_allocator();
256
    ////allocator.destroy<operations>(&ops_);
257
    ////allocator.construct<operations>(&ops_);
258
    ops_.reserve(op_count(source));
646✔
259
    const auto start = source.get_read_position();
646✔
260

261
    while (!source.is_exhausted())
83,858✔
262
    {
263
        ops_.emplace_back(source);
82,566✔
264
        prefail_ |= ops_.back().is_invalid();
82,566✔
265
    }
266

267
    size_ = source.get_read_position() - start;
646✔
268

269
    if (prefix)
646✔
270
    {
271
        source.set_limit();
517✔
272
        if (size_ != expected)
517✔
273
            source.invalidate();
2✔
274
    }
275

276
    valid_ = source;
646✔
277
    offset = ops_.begin();
646✔
278
}
646✔
279

280
// static/private
281
script script::from_string(const std::string& mnemonic) NOEXCEPT
1,498✔
282
{
283
    // There is always one operation per non-empty string token.
284
    auto tokens = split(mnemonic);
1,498✔
285
    auto prefail = false;
1,498✔
286

287
    // Split always returns at least one token, and when trimming it will be
288
    // empty only if there was nothing but whitespace in the mnemonic.
289
    if (tokens.front().empty())
1,498✔
290
        tokens.clear();
65✔
291

292
    operations ops;
1,498✔
293
    ops.reserve(tokens.size());
1,498✔
294

295
    // Create an op list from the split tokens.
296
    for (const auto& token: tokens)
12,187✔
297
    {
298
        ops.emplace_back(token);
10,693✔
299
        prefail |= ops.back().is_invalid();
10,693✔
300

301
        // This is a deserialization failure, not just an invalid code.
302
        if (!ops.back().is_valid())
10,693✔
303
            return {};
4✔
304
    }
305

306
    return { std::move(ops), prefail };
1,494✔
307
}
1,498✔
308

309
// Serialization.
310
// ----------------------------------------------------------------------------
311

312
data_chunk script::to_data(bool prefix) const NOEXCEPT
165✔
313
{
314
    data_chunk data(serialized_size(prefix));
165✔
315
    stream::out::copy ostream(data);
165✔
316
    to_data(ostream, prefix);
165✔
317
    return data;
330✔
318
}
165✔
319

320
void script::to_data(std::ostream& stream, bool prefix) const NOEXCEPT
165✔
321
{
322
    write::bytes::ostream out(stream);
165✔
323
    to_data(out, prefix);
165✔
324
}
165✔
325

326
// see also: subscript.to_data().
327
void script::to_data(writer& sink, bool prefix) const NOEXCEPT
3,332✔
328
{
329
    if (prefix)
3,332✔
330
        sink.write_variable(serialized_size(false));
3,150✔
331

332
    // Data serialization is affected by offset metadata.
333
    for (iterator op{ offset }; op != ops().end(); ++op)
1,494,758✔
334
        op->to_data(sink);
1,491,426✔
335
}
3,332✔
336

337
std::string script::to_string(uint32_t active_flags) const NOEXCEPT
28✔
338
{
339
    auto first = true;
28✔
340
    std::ostringstream text;
28✔
341

342
    // Throwing stream aborts.
343
    for (const auto& op: ops())
82✔
344
    {
345
        text << (first ? "" : " ") << op.to_string(active_flags);
80✔
346
        first = false;
54✔
347
    }
348

349
    // An invalid operation has a specialized serialization.
350
    return text.str();
28✔
351
}
28✔
352

353

354
// Properties.
355
// ----------------------------------------------------------------------------
356

357
bool script::is_valid() const NOEXCEPT
1,509✔
358
{
359
    // Any byte vector is a valid script.
360
    // This is false only if the byte count did not match the size prefix.
361
    return valid_;
1,509✔
362
}
363

364
bool script::is_prefail() const NOEXCEPT
3,087✔
365
{
366
    // The script contains an invalid opcode and will thus fail evaluation.
367
    return prefail_;
3,087✔
368
}
369

370
const operations& script::ops() const NOEXCEPT
31,304✔
371
{
372
    return ops_;
31,304✔
373
}
374

375
bool script::is_roller() const NOEXCEPT
8✔
376
{
377
    static const auto roll = operation{ opcode::roll };
8✔
378

379
    // Naive implementation, any op_roll in script, late-counted.
380
    // TODO: precompute on script parse, tune using performance profiling.
381
    return contains(ops_, roll);
8✔
382
};
383

384
// Consensus (witness::extract_script) and Electrum server payments key.
385
hash_digest script::hash() const NOEXCEPT
18✔
386
{
387
    hash_digest sha256{};
18✔
388
    hash::sha256::copy sink(sha256);
18✔
389
    to_data(sink, false);
18✔
390
    sink.flush();
18✔
391
    return sha256;
36✔
392
}
18✔
393

394
// static/private
395
size_t script::serialized_size(const operations& ops) NOEXCEPT
1,720✔
396
{
397
    return std::accumulate(ops.begin(), ops.end(), zero, op_size);
1,720✔
398
}
399

400
size_t script::serialized_size(bool prefix) const NOEXCEPT
8,732✔
401
{
402
    // Recompute it serialization has been affected by offset metadata.
403
    const auto size = (offset == ops_.begin()) ? size_ :
8,732✔
404
        std::accumulate(offset, ops_.end(), zero, op_size);
8✔
405

406
    return prefix ? ceilinged_add(size, variable_size(size)) : size;
8,732✔
407
}
408

409
// Utilities.
410
// ----------------------------------------------------------------------------
411

412
const data_chunk& script::witness_program() const NOEXCEPT
24✔
413
{
414
    static const data_chunk empty{};
24✔
415

416
    BC_PUSH_WARNING(NO_ARRAY_INDEXING)
417
    return is_witness_program_pattern(ops()) ? ops()[1].data() : empty;
24✔
418
    BC_POP_WARNING()
419
}
420

421
script_version script::version() const NOEXCEPT
48✔
422
{
423
    if (!is_witness_program_pattern(ops()))
48✔
424
        return script_version::unversioned;
425

426
    switch (ops_.front().code())
48✔
427
    {
428
        case opcode::push_size_0:
429
            return script_version::zero;
430
        default:
×
431
            return script_version::reserved;
×
432
    }
433
}
434

435
// Caller should test for is_sign_script_hash_pattern when sign_key_hash result
436
// as it is possible for an input script to match both patterns.
437
script_pattern script::pattern() const NOEXCEPT
11✔
438
{
439
    const auto input = output_pattern();
11✔
440
    return input == script_pattern::non_standard ? input_pattern() : input;
11✔
441
}
442

443
// Output patterns are mutually and input unambiguous.
444
// The bip141 coinbase pattern is not tested here, must test independently.
445
script_pattern script::output_pattern() const NOEXCEPT
22✔
446
{
447
    if (is_pay_key_hash_pattern(ops()))
22✔
448
        return script_pattern::pay_key_hash;
449

450
    if (is_pay_script_hash_pattern(ops()))
22✔
451
        return script_pattern::pay_script_hash;
452

453
    if (is_pay_null_data_pattern(ops()))
22✔
454
        return script_pattern::pay_null_data;
455

456
    if (is_pay_public_key_pattern(ops()))
18✔
457
        return script_pattern::pay_public_key;
458

459
    // Limited to 16 signatures though op_check_multisig allows 20.
460
    if (is_pay_multisig_pattern(ops()))
18✔
461
        return script_pattern::pay_multisig;
8✔
462

463
    return script_pattern::non_standard;
464
}
465

466
// A sign_key_hash result always implies sign_script_hash as well.
467
// The bip34 coinbase pattern is not tested here, must test independently.
468
script_pattern script::input_pattern() const NOEXCEPT
16✔
469
{
470
    if (is_sign_key_hash_pattern(ops()))
16✔
471
        return script_pattern::sign_key_hash;
472

473
    // This must follow is_sign_key_hash_pattern for ambiguity comment to hold.
474
    if (is_sign_script_hash_pattern(ops()))
16✔
475
        return script_pattern::sign_script_hash;
476

477
    if (is_sign_public_key_pattern(ops()))
16✔
478
        return script_pattern::sign_public_key;
479

480
    if (is_sign_multisig_pattern(ops()))
16✔
481
        return script_pattern::sign_multisig;
×
482

483
    return script_pattern::non_standard;
484
}
485

486
bool script::is_pay_to_witness(uint32_t active_flags) const NOEXCEPT
957✔
487
{
488
    // This is an optimization over using script::pattern.
489
    return is_enabled(active_flags, flags::bip141_rule) &&
1,413✔
490
        is_witness_program_pattern(ops());
456✔
491
}
492

493
bool script::is_pay_to_script_hash(uint32_t active_flags) const NOEXCEPT
965✔
494
{
495
    // This is an optimization over using script::pattern.
496
    return is_enabled(active_flags, flags::bip16_rule) &&
1,418✔
497
        is_pay_script_hash_pattern(ops());
453✔
498
}
499

500
// Count 1..16 multisig accurately for embedded (bip16) and witness (bip141).
501
constexpr size_t multisig_sigops(bool accurate, opcode code) NOEXCEPT
×
502
{
503
    return accurate && operation::is_positive(code) ?
×
504
        operation::opcode_to_positive(code) : multisig_default_sigops;
×
505
}
506

507
constexpr bool is_single_sigop(opcode code) NOEXCEPT
24✔
508
{
509
    return code == opcode::checksig || code == opcode::checksigverify;
24✔
510
}
511

512
constexpr bool is_multiple_sigop(opcode code) NOEXCEPT
×
513
{
514
    return code == opcode::checkmultisig || code == opcode::checkmultisigverify;
×
515
}
516

517
// TODO: compute in or at script evaluation and add coinbase input scripts.
518
// TODO: this precludes second deserialization of script for sigop counting.
519
size_t script::signature_operations(bool accurate) const NOEXCEPT
12✔
520
{
521
    auto total = zero;
12✔
522
    auto preceding = opcode::push_negative_1;
12✔
523

524
    for (const auto& op: ops())
36✔
525
    {
526
        const auto code = op.code();
24✔
527

528
        if (is_single_sigop(code))
24✔
529
            total = ceilinged_add(total, one);
24✔
530
        else if (is_multiple_sigop(code))
×
531
            total = ceilinged_add(total, multisig_sigops(accurate, preceding));
×
532

533
        preceding = code;
24✔
534
    }
535

536
    return total;
12✔
537
}
538

539
bool script::is_oversized() const NOEXCEPT
3,027✔
540
{
541
    return serialized_size(false) > max_script_size;
3,027✔
542
}
543

544
// An unspendable script is any that can provably not be spent under any
545
// circumstance. This allows for exclusion of the output as unspendable.
546
// The criteria below are not comprehensive but are fast to evaluate.
547
bool script::is_unspendable() const NOEXCEPT
3✔
548
{
549
    if (ops_.empty())
3✔
550
        return false;
551

552
    const auto& code = ops_.front().code();
3✔
553

554
    // There is no condition prior to the first opcode in a script, so
555
    // is_reserved must be checked. is_invalid short-circuits evaluation for
556
    // scripts that fail to parse, but would otherwise be caught in evaluation.
557
    return operation::is_reserved(code) || operation::is_invalid(code);
3✔
558
}
559

560
BC_POP_WARNING()
561

562
// JSON value convertors.
563
// ----------------------------------------------------------------------------
564

565
namespace json = boost::json;
566

567
// boost/json will soon have NOEXCEPT: github.com/boostorg/json/pull/636
568
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
569

570
script tag_invoke(json::value_to_tag<script>,
11✔
571
    const json::value& value) NOEXCEPT
572
{
573
    return script{ std::string(value.get_string().c_str()) };
11✔
574
}
575

576
void tag_invoke(json::value_from_tag, json::value& value,
22✔
577
    const script& script) NOEXCEPT
578
{
579
    value = script.to_string(flags::all_rules);
22✔
580
}
22✔
581

582
BC_POP_WARNING()
583

584
script::cptr tag_invoke(json::value_to_tag<script::cptr>,
×
585
    const json::value& value) NOEXCEPT
586
{
587
    return to_shared(tag_invoke(json::value_to_tag<script>{}, value));
×
588
}
589

590
// Shared pointer overload is required for navigation.
591
BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
592
BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)
593

594
void tag_invoke(json::value_from_tag tag, json::value& value,
×
595
    const script::cptr& script) NOEXCEPT
596
{
597
    tag_invoke(tag, value, *script);
×
598
}
×
599

600
BC_POP_WARNING()
601
BC_POP_WARNING()
602

603
} // namespace chain
604
} // namespace system
605
} // 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