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

libbitcoin / libbitcoin-system / 24054285573

06 Apr 2026 10:24PM UTC coverage: 81.681% (-0.005%) from 81.686%
24054285573

push

github

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

Make script::hash() return null_hash for invalid/default script.

1 of 2 new or added lines in 1 file covered. (50.0%)

11156 of 13658 relevant lines covered (81.68%)

3348618.84 hits per line

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

90.0
/src/chain/script.cpp
1
/**
2
 * Copyright (c) 2011-2026 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 <sstream>
23
#include <utility>
24
#include <bitcoin/system/chain/enums/flags.hpp>
25
#include <bitcoin/system/chain/enums/magic_numbers.hpp>
26
#include <bitcoin/system/chain/enums/opcode.hpp>
27
#include <bitcoin/system/chain/operation.hpp>
28
#include <bitcoin/system/data/data.hpp>
29
#include <bitcoin/system/define.hpp>
30
#include <bitcoin/system/hash/hash.hpp>
31
#include <bitcoin/system/machine/machine.hpp>
32
#include <bitcoin/system/stream/stream.hpp>
33

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

38
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
39
BC_PUSH_WARNING(NO_ARRAY_INDEXING)
40

41
// static
42
// This would be inlined but machine is a circular include.
43
//*****************************************************************************
44
// CONSENSUS: BIP34 requires coinbase input script to begin with one byte
45
// that indicates height size. This is inconsistent with an extreme future
46
// where the size byte overflows. However satoshi actually requires nominal
47
// encoding.
48
//*****************************************************************************
49
bool script::is_coinbase_pattern(const operations& ops, size_t height) NOEXCEPT
×
50
{
51
    using namespace machine::number;
×
52
    return !ops.empty()
×
53
        && ops[0].is_nominal_push()
×
54
        && ops[0].data() == chunk::from_integer(to_signed(height));
×
55
}
56

57
// static
58
// This would be constexpr but machine is a circular include.
59
// op_check_multisig is limited to 20 signatures (max_script_public_keys).
60
bool script::is_pay_multisig_pattern(const operations& ops) NOEXCEPT
20✔
61
{
62
    if (ops.size() < 4u || ops.back().code() != opcode::checkmultisig)
20✔
63
        return false;
4✔
64

65
    const auto signatures = [](const operation& op) NOEXCEPT
48✔
66
    {
67
        using namespace machine::number;
32✔
68
        if (op.is_positive())
32✔
69
            return operation::opcode_to_positive(op.code());
22✔
70

71
        int32_t count{};
10✔
72
        if (op.is_payload() && integer<4>::from_chunk(count, op.data()) &&
10✔
73
            !is_limited(count, one, max_script_public_keys))
8✔
74
            return narrow_sign_cast<uint8_t>(count);
6✔
75

76
        return 0_u8;
77
    };
78

79
    const auto m = signatures(ops.front());
16✔
80
    const auto n = signatures(ops[ops.size() - 2u]);
16✔
81
    if (is_zero(m) || is_zero(n) || (m > n) || (n != ops.size() - 3u))
16✔
82
        return false;
83

84
    for (auto op = std::next(ops.begin()); op != std::prev(ops.end(), 2); ++op)
100✔
85
        if (!is_public_key(op->data()))
90✔
86
            return false;
87

88
    return true;
89
}
90

91
// Constructors.
92
// ----------------------------------------------------------------------------
93

94
script::script() NOEXCEPT
129✔
95
  : script(operations{}, false, false, false, false, zero)
258✔
96
{
97
}
129✔
98

99
script::~script() NOEXCEPT
4,408✔
100
{
101
}
4,408✔
102

103
script::script(script&& other) NOEXCEPT
369✔
104
  : script(
105
      std::move(other.ops_),
369✔
106
      other.valid_,
369✔
107
      other.easier_,
369✔
108
      other.failer_,
369✔
109
      other.roller_,
369✔
110
      other.size_)
369✔
111
{
112
}
369✔
113

114
script::script(const script& other) NOEXCEPT
1,474✔
115
  : script(
116
      other.ops_,
1,474✔
117
      other.valid_,
1,474✔
118
      other.easier_,
1,474✔
119
      other.failer_,
1,474✔
120
      other.roller_,
1,474✔
121
      other.size_)
1,474✔
122
{
123
}
1,474✔
124

125
script::script(operations&& ops) NOEXCEPT
224✔
126
  : script(from_operations(std::move(ops)))
224✔
127
{
128
}
224✔
129

130
script::script(const operations& ops) NOEXCEPT
1✔
131
  : script(from_operations(ops))
1✔
132
{
133
}
1✔
134
    
135
script::script(const data_slice& data, bool prefix) NOEXCEPT
133✔
136
  : script(stream::in::fast(data), prefix)
133✔
137
{
138
}
133✔
139

140
// protected
141
script::script(stream::in::fast&& stream, bool prefix) NOEXCEPT
133✔
142
  : script(read::bytes::fast(stream), prefix)
133✔
143
{
144
}
133✔
145

146
script::script(stream::in::fast& stream, bool prefix) NOEXCEPT
1✔
147
  : script(read::bytes::fast(stream), prefix)
1✔
148
{
149
}
1✔
150

151
script::script(std::istream& stream, bool prefix) NOEXCEPT
1✔
152
  : script(read::bytes::istream(stream), prefix)
1✔
153
{
154
}
1✔
155

156
// protected
157
script::script(reader&& source, bool prefix) NOEXCEPT
135✔
158
  : script(source, prefix)
135✔
159
{
160
}
×
161

162
script::script(reader& source, bool prefix) NOEXCEPT
712✔
163
  : ops_(source.get_arena())
712✔
164
{
165
    assign_data(source, prefix);
712✔
166
}
712✔
167

168
script::script(const std::string_view& mnemonic, bool bitcoind) NOEXCEPT
1,503✔
169
  : script(from_string(mnemonic, bitcoind))
1,503✔
170
{
171
}
1,503✔
172

173
// protected
174
script::script(const operations& ops, bool valid, bool easier, bool failer,
3,696✔
175
    bool roller, size_t size) NOEXCEPT
1,972✔
176
  : ops_(ops),
3,696✔
177
    valid_(valid),
3,696✔
178
    easier_(easier),
3,696✔
179
    failer_(failer),
3,696✔
180
    roller_(roller),
3,696✔
181
    size_(size),
1,628✔
182
    offset(ops_.begin())
1,972✔
183
{
184
}
1,499✔
185

186
// Operators.
187
// ----------------------------------------------------------------------------
188

189
script& script::operator=(script&& other) NOEXCEPT
4✔
190
{
191
    ops_ = std::move(other.ops_);
4✔
192
    valid_ = other.valid_;
4✔
193
    easier_ = other.easier_;
4✔
194
    failer_ = other.failer_;
4✔
195
    roller_ = other.roller_;
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
    easier_ = other.easier_;
×
206
    failer_ = other.failer_;
×
207
    roller_ = other.roller_;
×
208
    size_ = other.size_;
×
209
    offset = ops_.begin();
×
210
    return *this;
×
211
}
212

213
bool script::operator==(const script& other) const NOEXCEPT
93✔
214
{
215
    return size_ == other.size_
93✔
216
        && ops_ == other.ops_;
93✔
217
}
218

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

224
// Deserialization.
225
// ----------------------------------------------------------------------------
226

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

234
    const auto start = source.get_read_position();
712✔
235
    auto count = zero;
712✔
236

237
    // This is expensive (1.1%) but far less than vector reallocs (11.6%).
238
    while (operation::count_op(source))
84,155✔
239
        ++count;
82,731✔
240

241
    source.set_position(start);
712✔
242
    return count;
712✔
243
}
244

245
// static/private
246
script script::from_operations(operations&& ops) NOEXCEPT
224✔
247
{
248
    constexpr auto valid = true;
224✔
249
    auto easier = false;
224✔
250
    auto failer = false;
224✔
251
    auto roller = false;
224✔
252

253
    for (const auto& op: ops)
709✔
254
    {
255
        easier |= op.is_success();
485✔
256
        failer |= op.is_invalid();
485✔
257
        roller |= op.is_roller();
485✔
258
    }
259

260
    const auto size = serialized_size(ops);
224✔
261
    return { std::move(ops), valid, easier, failer, roller, size };
224✔
262
}
263

264
// static/private
265
script script::from_operations(const operations& ops) NOEXCEPT
1✔
266
{
267
    constexpr auto valid = true;
1✔
268
    auto easier = false;
1✔
269
    auto failer = false;
1✔
270
    auto roller = false;
1✔
271

272
    for (const auto& op : ops)
3✔
273
    {
274
        easier |= op.is_success();
2✔
275
        failer |= op.is_invalid();
2✔
276
        roller |= op.is_roller();
2✔
277
    }
278

279
    const auto size = serialized_size(ops);
1✔
280
    return { ops, valid, easier, failer, roller, size };
1✔
281
}
282

283
// private
284
void script::assign_data(reader& source, bool prefix) NOEXCEPT
712✔
285
{
286
    easier_ = false;
712✔
287
    failer_ = false;
712✔
288
    roller_ = false;
712✔
289
    size_t expected{};
712✔
290

291
    if (prefix)
712✔
292
    {
293
        expected = source.read_size();
583✔
294
        source.set_limit(expected);
583✔
295
    }
296

297
    ops_.reserve(op_count(source));
712✔
298
    const auto start = source.get_read_position();
712✔
299

300
    while (!source.is_exhausted())
84,155✔
301
    {
302
        ops_.emplace_back(source);
82,731✔
303
        const auto& op = ops_.back();
82,731✔
304
        easier_ |= op.is_success();
82,731✔
305
        failer_ |= op.is_invalid();
82,731✔
306
        roller_ |= op.is_roller();
82,731✔
307
    }
308

309
    size_ = source.get_read_position() - start;
712✔
310

311
    if (prefix)
712✔
312
    {
313
        source.set_limit();
583✔
314
        if (size_ != expected)
583✔
315
            source.invalidate();
2✔
316
    }
317

318
    valid_ = source;
712✔
319
    offset = ops_.begin();
712✔
320
}
712✔
321

322
// static/private
323
script script::from_string(const std::string_view& mnemonic,
1,503✔
324
    bool /* bitcoind */) NOEXCEPT
325
{
326
    // TODO: incorporate bitcoind option.
327

328
    constexpr auto valid = true;
1,503✔
329
    auto easier = false;
1,503✔
330
    auto failer = false;
1,503✔
331
    auto roller = false;
1,503✔
332

333
    // There is always one operation per non-empty string token.
334
    auto tokens = split(mnemonic);
1,503✔
335

336
    // Split always returns at least one token, and when trimming it will be
337
    // empty only if there was nothing but whitespace in the mnemonic.
338
    if (tokens.front().empty())
1,503✔
339
        tokens.clear();
66✔
340

341
    operations ops{};
1,503✔
342
    ops.reserve(tokens.size());
1,503✔
343
    for (const auto& token: tokens)
12,228✔
344
    {
345
        ops.emplace_back(std::string_view{ token });
10,729✔
346
        const auto& op = ops.back();
10,729✔
347
        easier |= op.is_success();
10,729✔
348
        failer |= op.is_invalid();
10,729✔
349
        roller |= op.is_roller();
10,729✔
350

351
        // This is a deserialization failure, not just an invalid code.
352
        if (!ops.back().is_valid())
10,729✔
353
            return {};
4✔
354
    }
355

356
    const auto size = serialized_size(ops);
1,499✔
357
    return { std::move(ops), valid, easier, failer, roller, size };
1,499✔
358
}
1,503✔
359

360
// Serialization.
361
// ----------------------------------------------------------------------------
362

363
data_chunk script::to_data(bool prefix) const NOEXCEPT
169✔
364
{
365
    data_chunk data(serialized_size(prefix));
169✔
366
    stream::out::fast ostream(data);
169✔
367
    write::bytes::fast out(ostream);
169✔
368
    to_data(out, prefix);
169✔
369
    return data;
338✔
370
}
169✔
371

372
void script::to_data(std::ostream& stream, bool prefix) const NOEXCEPT
×
373
{
374
    write::bytes::ostream out(stream);
×
375
    to_data(out, prefix);
×
376
}
×
377

378
// see also: subscript.to_data().
379
void script::to_data(writer& sink, bool prefix) const NOEXCEPT
3,446✔
380
{
381
    if (prefix)
3,446✔
382
        sink.write_variable(serialized_size(false));
3,258✔
383

384
    // Data serialization is affected by offset metadata.
385
    for (iterator op{ offset }; op != ops().end(); ++op)
1,494,910✔
386
        op->to_data(sink);
1,491,464✔
387
}
3,446✔
388

389
std::string script::to_string(uint32_t active_flags,
32✔
390
    bool /* bitcoind */) const NOEXCEPT
391
{
392
    // TODO: incorporate bitcoind option.
393

394
    auto first = true;
32✔
395
    std::ostringstream text{};
32✔
396
    for (const auto& op: ops())
92✔
397
    {
398
        // Throwing stream aborts.
399
        text << (first ? "" : " ") << op.to_string(active_flags);
88✔
400
        first = false;
60✔
401
    }
402

403
    // An invalid operation has a specialized serialization.
404
    return text.str();
32✔
405
}
32✔
406

407
void script::clear_offset() const NOEXCEPT
3,095✔
408
{
409
    offset = ops_.begin();
3,095✔
410
}
3,095✔
411

412
// Properties.
413
// ----------------------------------------------------------------------------
414

415
bool script::is_valid() const NOEXCEPT
1,534✔
416
{
417
    // Any byte vector is a valid script.
418
    // This is false only if the byte count did not match the size prefix.
419
    return valid_;
1,534✔
420
}
421

422
bool script::is_roller() const NOEXCEPT
8✔
423
{
424
    return roller_;
8✔
425
};
426

427
bool script::is_prefail() const NOEXCEPT
6,188✔
428
{
429
    // Script contains an invalid opcode and will fail evaluation.
430
    return failer_;
6,188✔
431
}
432

433
bool script::is_prevalid() const NOEXCEPT
×
434
{
435
    // Script contains a success opcode and will pass evaluation (tapscript).
436
    return easier_;
×
437
}
438

439
bool script::is_underflow() const NOEXCEPT
3,095✔
440
{
441
    // Prefail implies an invalid code and a non-empty op stack.
442
    return is_prefail() && ops_.back().is_underflow();
3,095✔
443
}
444

445
bool script::is_oversized() const NOEXCEPT
3,095✔
446
{
447
    return serialized_size(false) > max_script_size;
3,095✔
448
}
449

450
// An unspendable script is any that can provably not be spent under any
451
// circumstance. This allows for exclusion of the output as unspendable.
452
// The criteria below are not comprehensive but are fast to evaluate.
453
bool script::is_unspendable() const NOEXCEPT
3✔
454
{
455
    if (ops_.empty())
3✔
456
        return false;
457

458
    const auto& code = ops_.front().code();
3✔
459

460
    // There is no condition prior to the first opcode in a script, so
461
    // is_reserved must be checked. is_invalid short-circuits evaluation for
462
    // scripts that fail to parse, but would otherwise be caught in evaluation.
463
    return operation::is_reserved(code) || operation::is_invalid(code);
3✔
464
}
465

466
const operations& script::ops() const NOEXCEPT
31,508✔
467
{
468
    return ops_;
31,508✔
469
}
470

471
// Consensus (witness::extract_script) and Electrum server payments key.
472
hash_digest script::hash() const NOEXCEPT
20✔
473
{
474
    // A failed parse or default initialized script will hash to null_hash.
475
    if (!is_valid())
20✔
NEW
476
        return {};
×
477

478
    hash_digest hash{};
20✔
479
    stream::out::fast stream{ hash };
20✔
480
    hash::sha256::fast sink(stream);
20✔
481
    to_data(sink, false);
20✔
482
    sink.flush();
20✔
483
    return hash;
20✔
484
}
20✔
485

486
// static/private
487
size_t script::serialized_size(const operations& ops) NOEXCEPT
1,724✔
488
{
489
    return std::accumulate(ops.begin(), ops.end(), zero, op_size);
1,724✔
490
}
491

492
size_t script::serialized_size(bool prefix) const NOEXCEPT
8,981✔
493
{
494
    // Recompute it serialization has been affected by offset metadata.
495
    const auto size = (offset == ops_.begin()) ? size_ :
8,981✔
496
        std::accumulate(offset, ops_.end(), zero, op_size);
7✔
497

498
    return prefix ? ceilinged_add(size, variable_size(size)) : size;
8,981✔
499
}
500

501
BC_POP_WARNING()
502
BC_POP_WARNING()
503

504
} // namespace chain
505
} // namespace system
506
} // 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