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

libbitcoin / libbitcoin-system / 9265864570

28 May 2024 08:16AM UTC coverage: 82.826% (-0.01%) from 82.837%
9265864570

push

github

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

Comments, style, use initializer.

50 of 58 new or added lines in 5 files covered. (86.21%)

2 existing lines in 1 file now uncovered.

9766 of 11791 relevant lines covered (82.83%)

4836714.76 hits per line

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

85.46
/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
// static (should be inline or constexpr).
51
// TODO: Avoiding circular include on machine.
NEW
52
bool script::is_coinbase_pattern(const operations& ops, size_t height) NOEXCEPT
×
53
{
54
    // TODO: number::chunk::from_int constexpr?
55
    return !ops.empty()
×
56
        && ops[0].is_nominal_push()
×
57
        && ops[0].data() == number::chunk::from_integer(to_unsigned(height));
×
58
}
59

60
// Constructors.
61
// ----------------------------------------------------------------------------
62

63
script::script() NOEXCEPT
116✔
64
  : script(operations{}, false, false, zero)
116✔
65
{
66
}
116✔
67

68
script::~script() NOEXCEPT
4,468✔
69
{
70
}
4,468✔
71

72
script::script(script&& other) NOEXCEPT
190✔
73
  : script(std::move(other.ops_), other.valid_, other.prefail_, other.size_)
190✔
74
{
75
}
190✔
76

77
script::script(const script& other) NOEXCEPT
1,470✔
78
  : script(other.ops_, other.valid_, other.prefail_, other.size_)
1,470✔
79
{
80
}
1,470✔
81

82
// Prefail is false.
83
script::script(operations&& ops) NOEXCEPT
59✔
84
  : script(std::move(ops), true, false, serialized_size(ops))
59✔
85
{
86
}
59✔
87

88
// Prefail is false.
89
script::script(const operations& ops) NOEXCEPT
1✔
90
  : script(ops, true, false, serialized_size(ops))
1✔
91
{
92
}
1✔
93

94
script::script(operations&& ops, bool prefail) NOEXCEPT
1,494✔
95
  : script(std::move(ops), true, prefail, serialized_size(ops))
1,494✔
96
{
97
}
1,494✔
98

99
script::script(const operations& ops, bool prefail) NOEXCEPT
×
NEW
100
  : script(ops, true, prefail, serialized_size(ops))
×
101
{
102
}
×
103

104
script::script(const data_slice& data, bool prefix) NOEXCEPT
133✔
105
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
106
  : script(stream::in::copy(data), prefix)
133✔
107
    BC_POP_WARNING()
108
{
109
}
133✔
110

111
////script::script(stream::in::fast&& stream, bool prefix) NOEXCEPT
112
////  : script(read::bytes::fast(stream), prefix)
113
////{
114
////}
115

116
script::script(stream::in::fast& stream, bool prefix) NOEXCEPT
1✔
117
  : script(read::bytes::fast(stream), prefix)
1✔
118
{
119
}
1✔
120

121
script::script(std::istream&& stream, bool prefix) NOEXCEPT
133✔
122
  : script(read::bytes::istream(stream), prefix)
133✔
123
{
124
}
133✔
125

126
script::script(std::istream& stream, bool prefix) NOEXCEPT
1✔
127
  : script(read::bytes::istream(stream), prefix)
1✔
128
{
129
}
1✔
130

131
script::script(reader&& source, bool prefix) NOEXCEPT
135✔
132
  : script(from_data(source, prefix))
135✔
133
{
134
}
135✔
135

136
script::script(reader& source, bool prefix) NOEXCEPT
480✔
137
  : script(from_data(source, prefix))
480✔
138
{
139
}
480✔
140

141
script::script(const std::string& mnemonic) NOEXCEPT
1,498✔
142
  : script(from_string(mnemonic))
1,498✔
143
{
144
}
1,498✔
145

146
// protected
147
script::script(operations&& ops, bool valid, bool prefail, size_t size) NOEXCEPT
2,474✔
148
  : ops_(std::move(ops)), valid_(valid), prefail_(prefail), size_(size),
731✔
149
    offset(ops_.begin())
2,474✔
150
{
UNCOV
151
}
×
152

153
// protected
154
script::script(const operations& ops, bool valid, bool prefail,
1,471✔
155
    size_t size) NOEXCEPT
1,471✔
156
  : ops_(ops), valid_(valid), prefail_(prefail), size_(size),
2,942✔
157
    offset(ops_.begin())
1,471✔
158
{
UNCOV
159
}
×
160

161
// Operators.
162
// ----------------------------------------------------------------------------
163

164
script& script::operator=(script&& other) NOEXCEPT
4✔
165
{
166
    ops_ = std::move(other.ops_);
4✔
167
    valid_ = other.valid_;
4✔
168
    prefail_ = other.prefail_;
4✔
169
    size_ = other.size_;
4✔
170
    offset = ops_.begin();
4✔
171
    return *this;
4✔
172
}
173

174
script& script::operator=(const script& other) NOEXCEPT
×
175
{
176
    ops_ = other.ops_;
×
177
    valid_ = other.valid_;
×
178
    prefail_ = other.prefail_;
×
NEW
179
    size_ = other.size_;
×
180
    offset = ops_.begin();
×
181
    return *this;
×
182
}
183

184
bool script::operator==(const script& other) const NOEXCEPT
63✔
185
{
186
    return size_ == other.size_
63✔
187
        && ops_ == other.ops_;
63✔
188
}
189

190
bool script::operator!=(const script& other) const NOEXCEPT
×
191
{
192
    return !(*this == other);
×
193
}
194

195
// Deserialization.
196
// ----------------------------------------------------------------------------
197

198
// static/private
199
size_t script::op_count(reader& source) NOEXCEPT
615✔
200
{
201
    // Stream errors reset by set_position so trap here.
202
    if (!source)
615✔
203
        return zero;
204

205
    const auto start = source.get_read_position();
615✔
206
    auto count = zero;
615✔
207

208
    // TODO: this is expensive (0.83%).
209
    while (operation::count_op(source))
83,732✔
210
        ++count;
82,502✔
211

212
    source.set_position(start);
615✔
213
    return count;
615✔
214
}
215

216
// static/private
217
script script::from_data(reader& source, bool prefix) NOEXCEPT
615✔
218
{
219
    auto expected = zero;
615✔
220
    auto prefail = false;
615✔
221

222
    if (prefix)
615✔
223
    {
224
        expected = source.read_size();
486✔
225
        source.set_limit(expected);
486✔
226
    }
227

228
    operations ops;
615✔
229
    ops.reserve(op_count(source));
615✔
230
    const auto start = source.get_read_position();
615✔
231

232
    while (!source.is_exhausted())
83,732✔
233
    {
234
        ops.emplace_back(source);
82,502✔
235
        prefail |= ops.back().is_invalid();
82,502✔
236
    }
237

238
    const auto size = source.get_read_position() - start;
615✔
239

240
    if (prefix)
615✔
241
    {
242
        source.set_limit();
486✔
243
        if (size != expected)
486✔
244
            source.invalidate();
2✔
245
    }
246

247
    return { std::move(ops), source, prefail, size };
615✔
248
}
615✔
249

250
// static/private
251
script script::from_string(const std::string& mnemonic) NOEXCEPT
1,498✔
252
{
253
    // There is always one operation per non-empty string token.
254
    auto tokens = split(mnemonic);
1,498✔
255
    auto prefail = false;
1,498✔
256

257
    // Split always returns at least one token, and when trimming it will be
258
    // empty only if there was nothing but whitespace in the mnemonic.
259
    if (tokens.front().empty())
1,498✔
260
        tokens.clear();
65✔
261

262
    operations ops;
1,498✔
263
    ops.reserve(tokens.size());
1,498✔
264

265
    // Create an op list from the split tokens.
266
    for (const auto& token: tokens)
12,187✔
267
    {
268
        ops.emplace_back(token);
10,693✔
269
        prefail |= ops.back().is_invalid();
10,693✔
270

271
        // This is a deserialization failure, not just an invalid code.
272
        if (!ops.back().is_valid())
10,693✔
273
            return {};
4✔
274
    }
275

276
    return { std::move(ops), prefail };
1,494✔
277
}
1,498✔
278

279
// Serialization.
280
// ----------------------------------------------------------------------------
281

282
data_chunk script::to_data(bool prefix) const NOEXCEPT
165✔
283
{
284
    data_chunk data(serialized_size(prefix));
165✔
285

286
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
287
    stream::out::copy ostream(data);
165✔
288
    BC_POP_WARNING()
289

290
    to_data(ostream, prefix);
165✔
291
    return data;
330✔
292
}
165✔
293

294
void script::to_data(std::ostream& stream, bool prefix) const NOEXCEPT
165✔
295
{
296
    write::bytes::ostream out(stream);
165✔
297
    to_data(out, prefix);
165✔
298
}
165✔
299

300
// see also: subscript.to_data().
301
void script::to_data(writer& sink, bool prefix) const NOEXCEPT
3,307✔
302
{
303
    if (prefix)
3,307✔
304
        sink.write_variable(serialized_size(false));
3,125✔
305

306
    // Data serialization is affected by offset metadata.
307
    for (iterator op{ offset }; op != ops().end(); ++op)
1,494,666✔
308
        op->to_data(sink);
1,491,359✔
309
}
3,307✔
310

311
std::string script::to_string(uint32_t active_flags) const NOEXCEPT
28✔
312
{
313
    auto first = true;
28✔
314
    std::ostringstream text;
28✔
315

316
    // Throwing stream aborts.
317
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
318

319
    for (const auto& op: ops())
82✔
320
    {
321
        text << (first ? "" : " ") << op.to_string(active_flags);
80✔
322
        first = false;
54✔
323
    }
324

325
    // An invalid operation has a specialized serialization.
326
    return text.str();
28✔
327

328
    BC_POP_WARNING()
329
}
28✔
330

331

332
// Properties.
333
// ----------------------------------------------------------------------------
334

335
bool script::is_valid() const NOEXCEPT
1,509✔
336
{
337
    // Any byte vector is a valid script.
338
    // This is false only if the byte count did not match the size prefix.
339
    return valid_;
1,509✔
340
}
341

342
bool script::is_prefail() const NOEXCEPT
3,087✔
343
{
344
    // The script contains an invalid opcode and will thus fail evaluation.
345
    return prefail_;
3,087✔
346
}
347

348
const operations& script::ops() const NOEXCEPT
31,312✔
349
{
350
    return ops_;
31,312✔
351
}
352

353
// Consensus (witness::extract_script) and Electrum server payments key.
354
hash_digest script::hash() const NOEXCEPT
18✔
355
{
356
    hash_digest sha256{};
18✔
357
    hash::sha256::copy sink(sha256);
18✔
358
    to_data(sink, false);
18✔
359
    sink.flush();
18✔
360
    return sha256;
36✔
361
}
18✔
362

363
// static
364
size_t script::serialized_size(const operations& ops) NOEXCEPT
1,554✔
365
{
366
    return std::accumulate(ops.begin(), ops.end(), zero, op_size);
1,554✔
367
}
368

369
size_t script::serialized_size(bool prefix) const NOEXCEPT
6,492✔
370
{
371
    size_t size = size_;
6,492✔
372

373
    // Recompute it serialization has been affected by offset metadata.
374
    if (offset != ops_.begin())
6,492✔
375
        size = std::accumulate(offset, ops_.end(), zero, op_size);
8✔
376

377
    if (prefix)
6,492✔
378
        size += variable_size(size);
168✔
379

380
    return size;
6,492✔
381
}
382

383
// Utilities.
384
// ----------------------------------------------------------------------------
385

386
const data_chunk& script::witness_program() const NOEXCEPT
24✔
387
{
388
    static const data_chunk empty;
24✔
389

390
    BC_PUSH_WARNING(NO_ARRAY_INDEXING)
391
    return is_witness_program_pattern(ops()) ? ops()[1].data() : empty;
24✔
392
    BC_POP_WARNING()
393
}
394

395
script_version script::version() const NOEXCEPT
48✔
396
{
397
    if (!is_witness_program_pattern(ops()))
48✔
398
        return script_version::unversioned;
399

400
    switch (ops_.front().code())
48✔
401
    {
402
        case opcode::push_size_0:
403
            return script_version::zero;
404
        default:
×
405
            return script_version::reserved;
×
406
    }
407
}
408

409
// Caller should test for is_sign_script_hash_pattern when sign_key_hash result
410
// as it is possible for an input script to match both patterns.
411
script_pattern script::pattern() const NOEXCEPT
11✔
412
{
413
    const auto input = output_pattern();
11✔
414
    return input == script_pattern::non_standard ? input_pattern() : input;
11✔
415
}
416

417
// Output patterns are mutually and input unambiguous.
418
// The bip141 coinbase pattern is not tested here, must test independently.
419
script_pattern script::output_pattern() const NOEXCEPT
22✔
420
{
421
    if (is_pay_key_hash_pattern(ops()))
22✔
422
        return script_pattern::pay_key_hash;
423

424
    if (is_pay_script_hash_pattern(ops()))
22✔
425
        return script_pattern::pay_script_hash;
426

427
    if (is_pay_null_data_pattern(ops()))
22✔
428
        return script_pattern::pay_null_data;
429

430
    if (is_pay_public_key_pattern(ops()))
18✔
431
        return script_pattern::pay_public_key;
432

433
    // Limited to 16 signatures though op_check_multisig allows 20.
434
    if (is_pay_multisig_pattern(ops()))
18✔
435
        return script_pattern::pay_multisig;
8✔
436

437
    return script_pattern::non_standard;
438
}
439

440
// A sign_key_hash result always implies sign_script_hash as well.
441
// The bip34 coinbase pattern is not tested here, must test independently.
442
script_pattern script::input_pattern() const NOEXCEPT
16✔
443
{
444
    if (is_sign_key_hash_pattern(ops()))
16✔
445
        return script_pattern::sign_key_hash;
446

447
    // This must follow is_sign_key_hash_pattern for ambiguity comment to hold.
448
    if (is_sign_script_hash_pattern(ops()))
16✔
449
        return script_pattern::sign_script_hash;
450

451
    if (is_sign_public_key_pattern(ops()))
16✔
452
        return script_pattern::sign_public_key;
453

454
    if (is_sign_multisig_pattern(ops()))
16✔
455
        return script_pattern::sign_multisig;
×
456

457
    return script_pattern::non_standard;
458
}
459

460
bool script::is_pay_to_witness(uint32_t active_flags) const NOEXCEPT
957✔
461
{
462
    // This is an optimization over using script::pattern.
463
    return is_enabled(active_flags, flags::bip141_rule) &&
1,413✔
464
        is_witness_program_pattern(ops());
456✔
465
}
466

467
bool script::is_pay_to_script_hash(uint32_t active_flags) const NOEXCEPT
965✔
468
{
469
    // This is an optimization over using script::pattern.
470
    return is_enabled(active_flags, flags::bip16_rule) &&
1,418✔
471
        is_pay_script_hash_pattern(ops());
453✔
472
}
473

474
// Count 1..16 multisig accurately for embedded (bip16) and witness (bip141).
475
constexpr size_t multisig_sigops(bool accurate, opcode code) NOEXCEPT
×
476
{
477
    return accurate && operation::is_positive(code) ?
×
478
        operation::opcode_to_positive(code) : multisig_default_sigops;
×
479
}
480

481
constexpr bool is_single_sigop(opcode code) NOEXCEPT
24✔
482
{
483
    return code == opcode::checksig || code == opcode::checksigverify;
24✔
484
}
485

486
constexpr bool is_multiple_sigop(opcode code) NOEXCEPT
×
487
{
488
    return code == opcode::checkmultisig || code == opcode::checkmultisigverify;
×
489
}
490

491
// TODO: compute in or at script evaluation and add coinbase input scripts.
492
// TODO: this precludes second deserialization of script for sigop counting.
493
size_t script::signature_operations(bool accurate) const NOEXCEPT
12✔
494
{
495
    auto total = zero;
12✔
496
    auto preceding = opcode::push_negative_1;
12✔
497

498
    for (const auto& op: ops())
36✔
499
    {
500
        const auto code = op.code();
24✔
501

502
        if (is_single_sigop(code))
24✔
503
            total = ceilinged_add(total, one);
24✔
504
        else if (is_multiple_sigop(code))
×
505
            total = ceilinged_add(total, multisig_sigops(accurate, preceding));
×
506

507
        preceding = code;
24✔
508
    }
509

510
    return total;
12✔
511
}
512

513
bool script::is_oversized() const NOEXCEPT
3,027✔
514
{
515
    return serialized_size(false) > max_script_size;
3,027✔
516
}
517

518
// An unspendable script is any that can provably not be spent under any
519
// circumstance. This allows for exclusion of the output as unspendable.
520
// The criteria below are not comprehensive but are fast to evaluate.
521
bool script::is_unspendable() const NOEXCEPT
3✔
522
{
523
    if (ops_.empty())
3✔
524
        return false;
525

526
    const auto& code = ops_.front().code();
3✔
527

528
    // There is no condition prior to the first opcode in a script, so
529
    // is_reserved must be checked. is_invalid short-circuits evaluation for
530
    // scripts that fail to parse, but would otherwise be caught in evaluation.
531
    return operation::is_reserved(code) || operation::is_invalid(code);
3✔
532
}
533

534
// JSON value convertors.
535
// ----------------------------------------------------------------------------
536

537
namespace json = boost::json;
538

539
// boost/json will soon have NOEXCEPT: github.com/boostorg/json/pull/636
540
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
541

542
script tag_invoke(json::value_to_tag<script>,
11✔
543
    const json::value& value) NOEXCEPT
544
{
545
    return script{ std::string(value.get_string().c_str()) };
11✔
546
}
547

548
void tag_invoke(json::value_from_tag, json::value& value,
22✔
549
    const script& script) NOEXCEPT
550
{
551
    value = script.to_string(flags::all_rules);
22✔
552
}
22✔
553

554
BC_POP_WARNING()
555

556
script::cptr tag_invoke(json::value_to_tag<script::cptr>,
×
557
    const json::value& value) NOEXCEPT
558
{
559
    return to_shared(tag_invoke(json::value_to_tag<script>{}, value));
×
560
}
561

562
// Shared pointer overload is required for navigation.
563
BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
564
BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)
565

566
void tag_invoke(json::value_from_tag tag, json::value& value,
×
567
    const script::cptr& script) NOEXCEPT
568
{
569
    tag_invoke(tag, value, *script);
×
570
}
×
571

572
BC_POP_WARNING()
573
BC_POP_WARNING()
574

575
} // namespace chain
576
} // namespace system
577
} // 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