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

libbitcoin / libbitcoin-system / 18823651969

26 Oct 2025 09:01PM UTC coverage: 80.946% (+0.04%) from 80.903%
18823651969

push

github

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

Simplify and optimize JSON annotation defines.

104 of 136 new or added lines in 16 files covered. (76.47%)

1 existing line in 1 file now uncovered.

10591 of 13084 relevant lines covered (80.95%)

3619070.67 hits per line

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

86.64
/src/chain/script.cpp
1
/**
2
 * Copyright (c) 2011-2025 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
// TODO: 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
// Constructors.
58
// ----------------------------------------------------------------------------
59

60
script::script() NOEXCEPT
130✔
61
  : script(operations{}, false, false, false, false, zero)
260✔
62
{
63
}
130✔
64

65
script::~script() NOEXCEPT
4,393✔
66
{
67
}
4,393✔
68

69
script::script(script&& other) NOEXCEPT
370✔
70
  : script(
71
      std::move(other.ops_),
370✔
72
      other.valid_,
370✔
73
      other.easier_,
370✔
74
      other.failer_,
370✔
75
      other.roller_,
370✔
76
      other.size_)
370✔
77
{
78
}
370✔
79

80
script::script(const script& other) NOEXCEPT
1,474✔
81
  : script(
82
      other.ops_,
1,474✔
83
      other.valid_,
1,474✔
84
      other.easier_,
1,474✔
85
      other.failer_,
1,474✔
86
      other.roller_,
1,474✔
87
      other.size_)
1,474✔
88
{
89
}
1,474✔
90

91
script::script(operations&& ops) NOEXCEPT
224✔
92
  : script(from_operations(std::move(ops)))
224✔
93
{
94
}
224✔
95

96
script::script(const operations& ops) NOEXCEPT
1✔
97
  : script(from_operations(ops))
1✔
98
{
99
}
1✔
100

101
script::script(stream::in::fast&& stream, bool prefix) NOEXCEPT
133✔
102
  : script(read::bytes::fast(stream), prefix)
133✔
103
{
104
}
133✔
105

106
script::script(stream::in::fast& stream, bool prefix) NOEXCEPT
1✔
107
  : script(read::bytes::fast(stream), prefix)
1✔
108
{
109
}
1✔
110

111
script::script(std::istream&& stream, bool prefix) NOEXCEPT
×
112
  : script(read::bytes::istream(stream), prefix)
×
113
{
114
}
×
115

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

121
script::script(reader&& source, bool prefix) NOEXCEPT
135✔
122
  : script(source, prefix)
135✔
123
{
124
}
×
125

126
script::script(reader& source, bool prefix) NOEXCEPT
696✔
127
  : ops_(source.get_arena())
696✔
128
{
129
    assign_data(source, prefix);
696✔
130
}
696✔
131

132
script::script(const std::string_view& mnemonic) NOEXCEPT
1,502✔
133
  : script(from_string(mnemonic))
1,502✔
134
{
135
}
1,502✔
136

137
// protected
138
script::script(const operations& ops, bool valid, bool easier, bool failer,
3,697✔
139
    bool roller, size_t size) NOEXCEPT
1,974✔
140
  : ops_(ops),
3,697✔
141
    valid_(valid),
3,697✔
142
    easier_(easier),
3,697✔
143
    failer_(failer),
3,697✔
144
    roller_(roller),
3,697✔
145
    size_(size),
1,628✔
146
    offset(ops_.begin())
1,974✔
147
{
148
}
1,498✔
149

150
// Operators.
151
// ----------------------------------------------------------------------------
152

153
script& script::operator=(script&& other) NOEXCEPT
4✔
154
{
155
    ops_ = std::move(other.ops_);
4✔
156
    valid_ = other.valid_;
4✔
157
    easier_ = other.easier_;
4✔
158
    failer_ = other.failer_;
4✔
159
    roller_ = other.roller_;
4✔
160
    size_ = other.size_;
4✔
161
    offset = ops_.begin();
4✔
162
    return *this;
4✔
163
}
164

165
script& script::operator=(const script& other) NOEXCEPT
×
166
{
167
    ops_ = other.ops_;
×
168
    valid_ = other.valid_;
×
169
    easier_ = other.easier_;
×
170
    failer_ = other.failer_;
×
171
    roller_ = other.roller_;
×
172
    size_ = other.size_;
×
173
    offset = ops_.begin();
×
174
    return *this;
×
175
}
176

177
bool script::operator==(const script& other) const NOEXCEPT
87✔
178
{
179
    return size_ == other.size_
87✔
180
        && ops_ == other.ops_;
87✔
181
}
182

183
bool script::operator!=(const script& other) const NOEXCEPT
×
184
{
185
    return !(*this == other);
×
186
}
187

188
// Deserialization.
189
// ----------------------------------------------------------------------------
190

191
// static/private
192
size_t script::op_count(reader& source) NOEXCEPT
696✔
193
{
194
    // Stream errors reset by set_position so trap here.
195
    if (!source)
696✔
196
        return zero;
197

198
    const auto start = source.get_read_position();
696✔
199
    auto count = zero;
696✔
200

201
    // This is expensive (1.1%) but far less than vector reallocs (11.6%).
202
    while (operation::count_op(source))
84,083✔
203
        ++count;
82,691✔
204

205
    source.set_position(start);
696✔
206
    return count;
696✔
207
}
208

209
// static/private
210
script script::from_operations(operations&& ops) NOEXCEPT
224✔
211
{
212
    constexpr auto valid = true;
224✔
213
    auto easier = false;
224✔
214
    auto failer = false;
224✔
215
    auto roller = false;
224✔
216

217
    for (const auto& op: ops)
709✔
218
    {
219
        easier |= op.is_success();
485✔
220
        failer |= op.is_invalid();
485✔
221
        roller |= op.is_roller();
485✔
222
    }
223

224
    const auto size = serialized_size(ops);
224✔
225
    return { std::move(ops), valid, easier, failer, roller, size };
224✔
226
}
227

228
// static/private
229
script script::from_operations(const operations& ops) NOEXCEPT
1✔
230
{
231
    constexpr auto valid = true;
1✔
232
    auto easier = false;
1✔
233
    auto failer = false;
1✔
234
    auto roller = false;
1✔
235

236
    for (const auto& op : ops)
3✔
237
    {
238
        easier |= op.is_success();
2✔
239
        failer |= op.is_invalid();
2✔
240
        roller |= op.is_roller();
2✔
241
    }
242

243
    const auto size = serialized_size(ops);
1✔
244
    return { ops, valid, easier, failer, roller, size };
1✔
245
}
246

247
// private
248
void script::assign_data(reader& source, bool prefix) NOEXCEPT
696✔
249
{
250
    easier_ = false;
696✔
251
    failer_ = false;
696✔
252
    roller_ = false;
696✔
253
    size_t expected{};
696✔
254

255
    if (prefix)
696✔
256
    {
257
        expected = source.read_size();
567✔
258
        source.set_limit(expected);
567✔
259
    }
260

261
    ops_.reserve(op_count(source));
696✔
262
    const auto start = source.get_read_position();
696✔
263

264
    while (!source.is_exhausted())
84,083✔
265
    {
266
        ops_.emplace_back(source);
82,691✔
267
        const auto& op = ops_.back();
82,691✔
268
        easier_ |= op.is_success();
82,691✔
269
        failer_ |= op.is_invalid();
82,691✔
270
        roller_ |= op.is_roller();
82,691✔
271
    }
272

273
    size_ = source.get_read_position() - start;
696✔
274

275
    if (prefix)
696✔
276
    {
277
        source.set_limit();
567✔
278
        if (size_ != expected)
567✔
279
            source.invalidate();
2✔
280
    }
281

282
    valid_ = source;
696✔
283
    offset = ops_.begin();
696✔
284
}
696✔
285

286
// static/private
287
script script::from_string(const std::string_view& mnemonic) NOEXCEPT
1,502✔
288
{
289
    constexpr auto valid = true;
1,502✔
290
    auto easier = false;
1,502✔
291
    auto failer = false;
1,502✔
292
    auto roller = false;
1,502✔
293

294
    // There is always one operation per non-empty string token.
295
    auto tokens = split(mnemonic);
1,502✔
296

297
    // Split always returns at least one token, and when trimming it will be
298
    // empty only if there was nothing but whitespace in the mnemonic.
299
    if (tokens.front().empty())
1,502✔
300
        tokens.clear();
66✔
301

302
    operations ops{};
1,502✔
303
    ops.reserve(tokens.size());
1,502✔
304

305
    // Create an op list from the split tokens.
306
    for (const auto& token: tokens)
12,200✔
307
    {
308
        ops.emplace_back(std::string_view{ token });
10,702✔
309
        const auto& op = ops.back();
10,702✔
310
        easier |= op.is_success();
10,702✔
311
        failer |= op.is_invalid();
10,702✔
312
        roller |= op.is_roller();
10,702✔
313

314
        // This is a deserialization failure, not just an invalid code.
315
        if (!ops.back().is_valid())
10,702✔
316
            return {};
4✔
317
    }
318

319
    const auto size = serialized_size(ops);
1,498✔
320
    return { std::move(ops), valid, easier, failer, roller, size };
1,498✔
321
}
1,502✔
322

323
// Serialization.
324
// ----------------------------------------------------------------------------
325

326
data_chunk script::to_data(bool prefix) const NOEXCEPT
165✔
327
{
328
    data_chunk data(serialized_size(prefix));
165✔
329
    stream::out::fast ostream(data);
165✔
330
    write::bytes::fast out(ostream);
165✔
331
    to_data(out, prefix);
165✔
332
    return data;
330✔
333
}
165✔
334

335
void script::to_data(std::ostream& stream, bool prefix) const NOEXCEPT
×
336
{
337
    write::bytes::ostream out(stream);
×
338
    to_data(out, prefix);
×
339
}
×
340

341
// see also: subscript.to_data().
342
void script::to_data(writer& sink, bool prefix) const NOEXCEPT
3,320✔
343
{
344
    if (prefix)
3,320✔
345
        sink.write_variable(serialized_size(false));
3,138✔
346

347
    // Data serialization is affected by offset metadata.
348
    for (iterator op{ offset }; op != ops().end(); ++op)
1,494,707✔
349
        op->to_data(sink);
1,491,387✔
350
}
3,320✔
351

352
std::string script::to_string(uint32_t active_flags) const NOEXCEPT
28✔
353
{
354
    auto first = true;
28✔
355
    std::ostringstream text;
28✔
356

357
    // Throwing stream aborts.
358
    for (const auto& op: ops())
82✔
359
    {
360
        text << (first ? "" : " ") << op.to_string(active_flags);
80✔
361
        first = false;
54✔
362
    }
363

364
    // An invalid operation has a specialized serialization.
365
    return text.str();
28✔
366
}
28✔
367

368
void script::clear_offset() const NOEXCEPT
3,095✔
369
{
370
    offset = ops_.begin();
3,095✔
371
}
3,095✔
372

373
// Properties.
374
// ----------------------------------------------------------------------------
375

376
bool script::is_valid() const NOEXCEPT
1,513✔
377
{
378
    // Any byte vector is a valid script.
379
    // This is false only if the byte count did not match the size prefix.
380
    return valid_;
1,513✔
381
}
382

383
bool script::is_roller() const NOEXCEPT
8✔
384
{
385
    return roller_;
8✔
386
};
387

388
bool script::is_prefail() const NOEXCEPT
6,188✔
389
{
390
    // Script contains an invalid opcode and will fail evaluation.
391
    return failer_;
6,188✔
392
}
393

394
bool script::is_prevalid() const NOEXCEPT
×
395
{
396
    // Script contains a success opcode and will pass evaluation (tapscript).
397
    return easier_;
×
398
}
399

400
bool script::is_underflow() const NOEXCEPT
3,095✔
401
{
402
    // Prefail implies an invalid code and a non-empty op stack.
403
    return is_prefail() && ops_.back().is_underflow();
3,095✔
404
}
405

406
bool script::is_oversized() const NOEXCEPT
3,095✔
407
{
408
    return serialized_size(false) > max_script_size;
3,095✔
409
}
410

411
// An unspendable script is any that can provably not be spent under any
412
// circumstance. This allows for exclusion of the output as unspendable.
413
// The criteria below are not comprehensive but are fast to evaluate.
414
bool script::is_unspendable() const NOEXCEPT
3✔
415
{
416
    if (ops_.empty())
3✔
417
        return false;
418

419
    const auto& code = ops_.front().code();
3✔
420

421
    // There is no condition prior to the first opcode in a script, so
422
    // is_reserved must be checked. is_invalid short-circuits evaluation for
423
    // scripts that fail to parse, but would otherwise be caught in evaluation.
424
    return operation::is_reserved(code) || operation::is_invalid(code);
3✔
425
}
426

427
const operations& script::ops() const NOEXCEPT
31,464✔
428
{
429
    return ops_;
31,464✔
430
}
431

432
// Consensus (witness::extract_script) and Electrum server payments key.
433
hash_digest script::hash() const NOEXCEPT
18✔
434
{
435
    hash_digest hash{};
18✔
436
    stream::out::fast stream{ hash };
18✔
437
    hash::sha256::fast sink(stream);
18✔
438
    to_data(sink, false);
18✔
439
    sink.flush();
18✔
440
    return hash;
36✔
441
}
18✔
442

443
// static/private
444
size_t script::serialized_size(const operations& ops) NOEXCEPT
1,723✔
445
{
446
    return std::accumulate(ops.begin(), ops.end(), zero, op_size);
1,723✔
447
}
448

449
size_t script::serialized_size(bool prefix) const NOEXCEPT
8,842✔
450
{
451
    // Recompute it serialization has been affected by offset metadata.
452
    const auto size = (offset == ops_.begin()) ? size_ :
8,842✔
453
        std::accumulate(offset, ops_.end(), zero, op_size);
7✔
454

455
    return prefix ? ceilinged_add(size, variable_size(size)) : size;
8,842✔
456
}
457

458
BC_POP_WARNING()
459
BC_POP_WARNING()
460

461
// JSON value convertors.
462
// ----------------------------------------------------------------------------
463

464
DEFINE_JSON_TO_TAG(script)
11✔
465
{
466
    return script{ value.as_string() };
11✔
467
}
468

469
DEFINE_JSON_FROM_TAG(script)
22✔
470
{
471
    // TODO: inject rules.
472
    value = instance.to_string(flags::all_rules);
22✔
473
}
22✔
474

NEW
475
DEFINE_JSON_TO_TAG(script::cptr)
×
476
{
NEW
477
    return to_shared(tag_invoke(to_tag<script>{}, value));
×
478
}
479

NEW
480
DEFINE_JSON_FROM_TAG(script::cptr)
×
481
{
NEW
482
    tag_invoke(from_tag{}, value, *instance);
×
483
}
×
484

485
} // namespace chain
486
} // namespace system
487
} // 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