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

libbitcoin / libbitcoin-system / 14845904192

05 May 2025 08:45PM UTC coverage: 82.712% (-0.08%) from 82.789%
14845904192

push

github

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

Stub in op_check_sig_add() and op_check_schnorr_sig(), update tests.

230 of 274 new or added lines in 7 files covered. (83.94%)

10200 of 12332 relevant lines covered (82.71%)

3906935.59 hits per line

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

80.88
/include/bitcoin/system/impl/machine/stack.ipp
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
#ifndef LIBBITCOIN_SYSTEM_MACHINE_STACK_IPP
20
#define LIBBITCOIN_SYSTEM_MACHINE_STACK_IPP
21

22
#include <tuple>
23
#include <utility>
24
#include <bitcoin/system/data/data.hpp>
25
#include <bitcoin/system/define.hpp>
26
#include <bitcoin/system/math/math.hpp>
27

28
namespace libbitcoin {
29
namespace system {
30
namespace machine {
31

32
TEMPLATE
33
INLINE CLASS::
1,562✔
34
stack() NOEXCEPT
35
  : container_{}, tether_{}
1,562✔
36
{
37
}
38

39
TEMPLATE
40
INLINE CLASS::
23✔
41
stack(Container&& container) NOEXCEPT
42
  : container_(std::move(container)), tether_{}
23✔
43
{
44
}
45

46
// Pure stack abstraction.
47
// ----------------------------------------------------------------------------
48

49
TEMPLATE
50
INLINE const stack_variant& CLASS::
3,578✔
51
top() const NOEXCEPT
52
{
53
    BC_ASSERT(!empty());
54
    return container_.back();
35✔
55
}
56

57
TEMPLATE
58
INLINE stack_variant CLASS::
1,141✔
59
pop() NOEXCEPT
60
{
61
    BC_ASSERT(!empty());
62
    stack_variant temporary{ std::move(container_.back()) };
1,141✔
63
    container_.pop_back();
1,141✔
64
    return temporary;
1,141✔
65
}
66

67
TEMPLATE
68
INLINE void CLASS::
10,182✔
69
drop() NOEXCEPT
70
{
71
    BC_ASSERT(!empty());
72
    container_.pop_back();
745✔
73
}
74

75
TEMPLATE
76
INLINE bool CLASS::
7,201✔
77
empty() const NOEXCEPT
78
{
79
    return container_.empty();
1,088✔
80
}
81

82
TEMPLATE
83
INLINE size_t CLASS::
33,670✔
84
size() const NOEXCEPT
85
{
86
    return container_.size();
76✔
87
}
88

89
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
90

91
TEMPLATE
92
INLINE void CLASS::
168✔
93
push(data_chunk&& value) NOEXCEPT
94
{
95
    container_.push_back(make_external(std::move(value), tether_));
169✔
96
}
97

98
TEMPLATE
99
INLINE void CLASS::
2✔
100
push(stack_variant&& value) NOEXCEPT
101
{
102
    container_.push_back(std::move(value));
2✔
103
}
104

105
TEMPLATE
106
INLINE void CLASS::
9,957✔
107
push(const stack_variant& value) NOEXCEPT
108
{
109
    container_.push_back(value);
9,957✔
110
}
111

112
TEMPLATE
113
INLINE void CLASS::
2,018✔
114
emplace_boolean(bool value) NOEXCEPT
115
{
116
    container_.emplace_back(value);
2,018✔
117
}
118

119
TEMPLATE
120
INLINE void CLASS::
7,256✔
121
emplace_integer(int64_t value) NOEXCEPT
122
{
123
    container_.emplace_back(value);
7,256✔
124
}
125

126
TEMPLATE
127
INLINE void CLASS::
4,474✔
128
emplace_chunk(const chunk_xptr& value) NOEXCEPT
129
{
130
    container_.emplace_back(value.get());
4,474✔
131
}
132

133
BC_POP_WARNING()
134

135
// Positional (stack cheats).
136
// ----------------------------------------------------------------------------
137
// These optimizations prevent used of std::stack.
138

139
TEMPLATE
140
INLINE void CLASS::
16✔
141
erase(size_t index) NOEXCEPT
142
{
143
    BC_ASSERT(index < size());
144
    container_.erase(std::prev(container_.end(), add1(index)));
16✔
145
}
146

147
TEMPLATE
148
INLINE void CLASS::
174✔
149
swap(size_t left_index, size_t right_index) NOEXCEPT
150
{
151
    BC_ASSERT(left_index < size() && right_index < size());
152

153
    if constexpr (vector_)
154
    {
155
        const auto back = sub1(size());
156
        std::swap(
174✔
157
            container_[back - left_index],
174✔
158
            container_[back - right_index]);
174✔
159
    }
160

161
    if constexpr (linked_)
162
    {
163
        const auto end = container_.end();
164
        std::swap(
×
165
            *std::prev(end, add1(left_index)),
×
166
            *std::prev(end, add1(right_index)));
×
167
    }
168
}
169

170
TEMPLATE
171
INLINE const stack_variant& CLASS::
6,616✔
172
peek(size_t index) const NOEXCEPT
173
{
174
    BC_ASSERT(index < size());
175

176
    if constexpr (vector_)
177
        return container_[sub1(size()) - index];
9,916✔
178

179
    if constexpr (linked_)
180
        return *std::prev(container_.end(), add1(index));
×
181
}
182

183
// Aliases.
184
// ----------------------------------------------------------------------------
185

186
TEMPLATE
187
INLINE bool CLASS::
4,738✔
188
peek_signed4(int32_t& value) const NOEXCEPT
189
{
190
    return peek_signed<4>(value);
4,738✔
191
}
192

193
TEMPLATE
194
INLINE bool CLASS::
12✔
195
peek_signed5(int64_t& value) const NOEXCEPT
196
{
197
    return peek_signed<5>(value);
12✔
198
}
199

200
/// Variant data conversions.
201
// ----------------------------------------------------------------------------
202

203
// Generalized integer peek for varying bit widths up to 8 bytes.
204
// Chunk to integer conversions are constrained by caller (4 or 5 bytes).
205
TEMPLATE
206
template<size_t Bytes, typename Integer,
207
    if_not_lesser<sizeof(Integer), Bytes>,
208
    if_signed_integral_integer<Integer>>
209
inline bool CLASS::
4,750✔
210
peek_signed(Integer& value) const NOEXCEPT
211
{
212
    using namespace number;
213
    auto result{ true };
4,750✔
214

215
    std::visit(overload
4,750✔
216
    {
217
        [&](bool vary) NOEXCEPT
80✔
218
        {
219
            // This is never executed in standard scripts.
220
            value = to_int(vary);
40✔
221
        },
222
        [&](int64_t vary) NOEXCEPT
8,838✔
223
        {
224
            // This is the canonical use case (bounds check only).
225
            result = integer<Bytes>::from_integer(value, vary);
4,419✔
226
        },
227
        [&](const chunk_xptr& vary) NOEXCEPT
291✔
228
        {
229
            // This is never executed in standard scripts.
230
            result = integer<Bytes>::from_chunk(value, *vary);
291✔
231
        }
232
    }, top());
233

234
    return result;
4,750✔
235
}
236

237
TEMPLATE
238
inline bool CLASS::
1,593✔
239
peek_bool() const NOEXCEPT
240
{
241
    using namespace number;
242
    bool value{};
1,593✔
243

244
    std::visit(overload
1,593✔
245
    {
246
        [&](bool vary) NOEXCEPT
713✔
247
        {
248
            // This is the canonical use case.
249
            value = vary;
713✔
250
        },
251
        [&](int64_t vary) NOEXCEPT
828✔
252
        {
253
            // This is never executed in standard scripts.
254
            value = boolean::from_integer(vary);
828✔
255
        },
256
        [&](const chunk_xptr& vary) NOEXCEPT
52✔
257
        {
258
            // This is never executed in standard scripts.
259
            value = boolean::from_chunk(*vary);
52✔
260
        }
261
    }, top());
262

263
    return value;
1,593✔
264
}
265

266
// Differs from peek_bool in that a chunk false must be empty [].
267
TEMPLATE
268
inline bool CLASS::
2,050✔
269
peek_strict_bool() const NOEXCEPT
270
{
271
    using namespace number;
272
    bool value{};
2,050✔
273

274
    std::visit(overload
2,050✔
275
    {
276
        [&](bool vary) NOEXCEPT
1,200✔
277
        {
278
            // This is the canonical use case (after bip147).
279
            value = vary;
1,200✔
280
        },
281
        [&](int64_t vary) NOEXCEPT
848✔
282
        {
283
            // This may be executed in standard scripts (before bip147).
284
            value = boolean::from_integer(vary);
848✔
285
        },
286
        [&](const chunk_xptr& vary) NOEXCEPT
2✔
287
        {
288
            // This may be executed in standard scripts (before bip147).
289
            value = boolean::from_chunk_strict(*vary);
2✔
290
        }
291
    }, top());
292

293
    return value;
2,050✔
294
}
295

296
// A chunk false must be [] and chunk true must be [0x01], otherwise fails.
297
TEMPLATE
NEW
298
inline bool CLASS::
×
299
peek_minimal_bool(bool& value) const NOEXCEPT
300
{
301
    using namespace number;
NEW
302
    auto result{ true };
×
303

NEW
304
    std::visit(overload
×
305
    {
NEW
306
        [&](bool vary) NOEXCEPT
×
307
        {
308
            // This is the canonical use case (tapscript).
NEW
309
            value = vary;
×
310
        },
NEW
311
        [&](int64_t vary) NOEXCEPT
×
312
        {
313
            // This may be executed in tapscripts (standard).
NEW
314
            result = boolean::from_integer(value, vary);
×
315
        },
NEW
316
        [&](const chunk_xptr& vary) NOEXCEPT
×
317
        {
318
            // This may be executed in tapscripts (standard).
NEW
319
            result = boolean::from_chunk(value, *vary);
×
320
        }
321
    }, top());
322

NEW
323
    return result;
×
324
}
325

326
// This is the only source of peek/pop (read) tethering.
327
TEMPLATE
328
inline chunk_xptr CLASS::
3,578✔
329
peek_chunk() const NOEXCEPT
330
{
331
    using namespace number;
332
    chunk_xptr value{};
3,578✔
333

334
    std::visit(overload
3,578✔
335
    {
NEW
336
        [&](bool vary) NOEXCEPT
×
337
        {
338
            // This is never executed in standard scripts.
339
            value = make_external(chunk::from_bool(vary), tether_);
×
340
        },
341
        [&](int64_t vary) NOEXCEPT
76✔
342
        {
343
            // This is never executed in standard scripts.
344
            value = make_external(chunk::from_integer(vary), tether_);
38✔
345
        },
346
        [&](const chunk_xptr& vary) NOEXCEPT
3,540✔
347
        {
348
            // This is the canonical use case.
349
            value = vary;
3,540✔
350
        }
351
    }, top());
352

353
    return value;
3,578✔
354
}
355

356
// Could use peek_chunk but this overload skips allocation and tethering.
357
TEMPLATE
358
inline size_t CLASS::
58✔
359
peek_size() const NOEXCEPT
360
{
361
    using namespace number;
362
    size_t value{};
58✔
363

364
    std::visit(overload
58✔
365
    {
366
        [&](bool vary) NOEXCEPT
4✔
367
        {
368
            // This is never executed in standard scripts.
369
            value = chunk::from_bool(vary).size();
2✔
370
        },
371
        [&](int64_t vary) NOEXCEPT
20✔
372
        {
373
            // This is never executed in standard scripts.
374
            value = chunk::from_integer(vary).size();
10✔
375
        },
376
        [&](const chunk_xptr& vary) NOEXCEPT
46✔
377
        {
378
            // This is never executed in standard scripts.
379
            value = vary->size();
46✔
380
        }
381
    }, top());
382

383
    return value;
58✔
384
}
385

386
/// Static variant compare with conversion.
387
/// Integers are unconstrained as these are stack chunk equality comparisons.
388
TEMPLATE
389
inline bool CLASS::
560✔
390
equal_chunks(const stack_variant& left,
391
    const stack_variant& right) NOEXCEPT
392
{
393
    enum stack_type { bool_, int64_, pchunk_ };
394
    static_assert(std::variant_size<stack_variant>::value == 3u);
395
    const auto right_type = static_cast<stack_type>(right.index());
560✔
396

397
    using namespace number;
398
    auto same{ true };
560✔
399

400
    // Methods bound at compile time (free).
401
    // One runtime switch on variant index (cheap).
402
    // bool/int conversions are compile-time (free).
403
    // chunk conversions reduce to conventional bitcoin design.
404
    std::visit(overload
560✔
405
    {
406
        // This is never executed in standard scripts.
407
        [&](bool vary) NOEXCEPT
24✔
408
        {
409
            switch (right_type)
12✔
410
            {
411
                case bool_:
×
412
                    same = std::get<bool>(right) == vary;
×
413
                    break;
×
414
                case int64_:
12✔
415
                    same = std::get<int64_t>(right) == to_int(vary);
12✔
416
                    break;
12✔
417
                case pchunk_:
×
418
                    same = *std::get<chunk_xptr>(right) == chunk::from_bool(vary);
×
419
            }
420
        },
421

422
        // This is never executed in standard scripts.
423
        [&](int64_t vary) NOEXCEPT
700✔
424
        {
425
            switch (right_type)
350✔
426
            {
427
                case bool_:
×
NEW
428
                    same = to_int(std::get<bool>(right)) == vary;
×
429
                    break;
×
430
                case int64_:
322✔
431
                    same = std::get<int64_t>(right) == vary;
322✔
432
                    break;
322✔
433
                case pchunk_:
28✔
434
                    same = *std::get<chunk_xptr>(right) == chunk::from_integer(vary);
28✔
435
            }
436
        },
437

438
        // This is the canonical use case.
439
        [&](chunk_xptr vary) NOEXCEPT
396✔
440
        {
441
            switch (right_type)
198✔
442
            {
443
                case bool_:
444
                    // This is never executed in standard scripts.
445
                    same = chunk::from_bool(std::get<bool>(right)) == *vary;
×
446
                    break;
×
447
                case int64_:
448
                    // This is never executed in standard scripts.
449
                    same = chunk::from_integer(std::get<int64_t>(right)) == *vary;
14✔
450
                    break;
14✔
451
                case pchunk_:
452
                    // This is the canonical use case.
453
                    same = *std::get<chunk_xptr>(right) == *vary;
184✔
454
            }
455
        }
456
    }, left);
457

458
    return same;
560✔
459
}
460

461
} // namespace machine
462
} // namespace system
463
} // namespace libbitcoin
464

465
#endif
466

467
// Tethering Considerations
468
//
469
// Hash results and int/bool->chunks are saved using a shared_ptr vector.
470
// The tether is not garbage-collected (until destruct) as this is a space-
471
// time performance tradeoff. The maximum number of constructable chunks is
472
// bound by the script size limit. A standard in/out script pair tethers
473
// only one chunk, the computed hash.
474
//
475
// The following script operations will ALWAYS tether chunks, as the result
476
// of a computed hash is *pushed* to the weak pointer (xptr) variant stack.
477
//
478
// op_ripemd160         (1)
479
// op_sha1              (1)
480
// op_sha256            (1)
481
// op_hash160           (1)
482
// op_hash256           (1)
483
//
484
// The following script operations will ONLY tether chunks in the case where
485
// the *popped* element was originally bool/int64_t but required as chunk.
486
// This is never the case in standard scripts.
487
//
488
// op_ripemd160         (0..1)
489
// op_sha1              (0..1)
490
// op_sha256            (0..1)
491
// op_hash160           (0..1)
492
// op_hash256           (0..1)
493
// op_size              (0..1)
494
// op_check_sig         (0..2, and m (endorsements) + n (keys))
495
// op_check_sig_verify  (0..2, and m (endorsements) + n (keys))
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