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

libbitcoin / libbitcoin-system / 9950156475

16 Jul 2024 03:16AM UTC coverage: 83.203% (+0.3%) from 82.874%
9950156475

push

github

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

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

79.38
/src/chain/input.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/input.hpp>
20

21
#include <algorithm>
22
#include <memory>
23
#include <utility>
24
#include <bitcoin/system/chain/context.hpp>
25
#include <bitcoin/system/chain/enums/magic_numbers.hpp>
26
#include <bitcoin/system/chain/point.hpp>
27
#include <bitcoin/system/chain/prevout.hpp>
28
#include <bitcoin/system/chain/script.hpp>
29
#include <bitcoin/system/chain/witness.hpp>
30
#include <bitcoin/system/define.hpp>
31
#include <bitcoin/system/math/math.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

40
// Product overflows guarded by script size limit.
41
static_assert(max_script_size < 
42
    max_size_t / multisig_default_sigops / heavy_sigops_factor,
43
    "input sigop overflow guard");
44

45
// Constructors.
46
// ----------------------------------------------------------------------------
47

48
// Default point is null_hash and point::null_index. 
49
// Default metadata is spent, invalid, max_size_t value. 
50
input::input() NOEXCEPT
22✔
51
  : input(
52
      to_shared<chain::point>(),
22✔
53
      to_shared<chain::script>(),
22✔
54
      to_shared<chain::witness>(),
22✔
55
      0, false)
88✔
56
{
57
}
22✔
58

59
input::input(chain::point&& point, chain::script&& script,
160✔
60
    uint32_t sequence) NOEXCEPT
160✔
61
  : input(
62
      to_shared(std::move(point)),
160✔
63
      to_shared(std::move(script)),
160✔
64
      to_shared<chain::witness>(),
160✔
65
      sequence, true)
320✔
66
{
67
}
160✔
68

69
input::input(const chain::point& point, const chain::script& script,
734✔
70
    uint32_t sequence) NOEXCEPT
734✔
71
  : input(
72
      to_shared(point),
734✔
73
      to_shared(script),
734✔
74
      to_shared<chain::witness>(),
734✔
75
      sequence, true)
1,468✔
76
{
77
}
734✔
78

79
input::input(const chain::point::cptr& point,
×
80
    const chain::script::cptr& script, uint32_t sequence) NOEXCEPT
×
81
  : input(
82
      point ? point : to_shared<chain::point>(),
×
83
      script ? script : to_shared<chain::script>(),
×
84
      to_shared<chain::witness>(),
×
85
      sequence, true)
×
86
{
87
}
×
88

89
input::input(chain::point&& point, chain::script&& script,
14✔
90
    chain::witness&& witness, uint32_t sequence) NOEXCEPT
14✔
91
  : input(
92
      to_shared(std::move(point)),
14✔
93
      to_shared(std::move(script)),
14✔
94
      to_shared(std::move(witness)),
14✔
95
      sequence, true)
14✔
96
{
97
}
14✔
98

99
input::input(const chain::point& point, const chain::script& script,
1✔
100
    const chain::witness& witness, uint32_t sequence) NOEXCEPT
1✔
101
  : input(
102
      to_shared(point),
1✔
103
      to_shared(script),
1✔
104
      to_shared(witness),
1✔
105
      sequence, true)
1✔
106
{
107
}
1✔
108

109
input::input(const chain::point::cptr& point, const chain::script::cptr& script,
×
110
    const chain::witness::cptr& witness, uint32_t sequence) NOEXCEPT
×
111
  : input(point, script, witness, sequence, true)
×
112
{
113
}
×
114

115
input::input(const data_slice& data) NOEXCEPT
5✔
116
  : input(stream::in::copy(data))
5✔
117
{
118
}
5✔
119

120
////input::input(stream::in::fast&& stream) NOEXCEPT
121
////  : input(read::bytes::fast(stream))
122
////{
123
////}
124

125
input::input(stream::in::fast& stream) NOEXCEPT
1✔
126
  : input(read::bytes::fast(stream))
1✔
127
{
128
}
1✔
129

130
input::input(std::istream&& stream) NOEXCEPT
5✔
131
  : input(read::bytes::istream(stream))
5✔
132
{
133
}
5✔
134

135
input::input(std::istream& stream) NOEXCEPT
3✔
136
  : input(read::bytes::istream(stream))
3✔
137
{
138
}
3✔
139

140
input::input(reader&& source) NOEXCEPT
9✔
141
  : input(source/*from_data(source)*/)
9✔
142
{
UNCOV
143
}
×
144

145
// Witness is deserialized and assigned by transaction.
146
input::input(reader& source) NOEXCEPT
255✔
147
////: input(from_data(source))
148
  : point_(
255✔
149
        source.get_allocator().new_object<chain::point>(source),
255✔
150
        source.get_allocator().deleter<chain::point>(source.get_arena())),
255✔
151
    script_(
510✔
152
        source.get_allocator().new_object<chain::script>(source, true),
255✔
153
        source.get_allocator().deleter<chain::script>(source.get_arena())),
255✔
154
    witness_(
255✔
155
        source.get_allocator().new_object<chain::witness>(/*empty*/),
255✔
156
        source.get_allocator().deleter<chain::witness>(source.get_arena())),
255✔
157
    sequence_(source.read_4_bytes_little_endian()),
255✔
158
    valid_(source),
255✔
159
    size_(serialized_size(*script_, *witness_))
510✔
160
{
161
    ////assign_data(source);
162
}
255✔
163

164
// protected
165
input::input(const chain::point::cptr& point, const chain::script::cptr& script,
931✔
166
    const chain::witness::cptr& witness, uint32_t sequence, bool valid) NOEXCEPT
931✔
167
  : point_(point),
168
    script_(script),
169
    witness_(witness),
170
    sequence_(sequence),
931✔
171
    valid_(valid),
931✔
172
    size_(serialized_size(*script, *witness))
1,862✔
173
{
174
}
931✔
175

176
// Operators.
177
// ----------------------------------------------------------------------------
178

179
bool input::operator==(const input& other) const NOEXCEPT
43✔
180
{
181
    return (sequence_ == other.sequence_)
43✔
182
        && (point_ == other.point_ || *point_ == *other.point_)
41✔
183
        && (script_ == other.script_ || *script_ == *other.script_)
41✔
184
        && (witness_ == other.witness_ || *witness_ == *other.witness_);
84✔
185
}
186

187
bool input::operator!=(const input& other) const NOEXCEPT
2✔
188
{
189
    return !(*this == other);
2✔
190
}
191

192
// Deserialization.
193
// ----------------------------------------------------------------------------
194

195
////// static/private
196
////input input::from_data(reader& source) NOEXCEPT
197
////{
198
////    // Witness is deserialized by transaction.
199
////    return
200
////    {
201
////        to_shared<chain::point>(source),
202
////        to_shared<chain::script>(source, true),
203
////        to_shared<chain::witness>(),
204
////        source.read_4_bytes_little_endian(),
205
////        source
206
////    };
207
////}
208

209
// private
NEW
210
void input::assign_data(reader&) NOEXCEPT
×
211
{
212
    ////auto& allocator = source.get_allocator();
213
    ////
214
    ////allocator.construct<chain::point::cptr>(&point_,
215
    ////    allocator.new_object<chain::point>(source),
216
    ////    allocator.deleter<chain::point>(source.get_arena()));
217
    ////
218
    ////allocator.construct<chain::script::cptr>(&script_,
219
    ////    allocator.new_object<chain::script>(source, true),
220
    ////    allocator.deleter<chain::script>(source.get_arena()));
221
    ////
222
    ////// Witness is deserialized and assigned by transaction.
223
    ////allocator.construct<chain::witness::cptr>(&witness_,
224
    ////    allocator.new_object<chain::witness>(/*empty*/),
225
    ////    allocator.deleter<chain::witness>(source.get_arena()));
226
    ////
227
    ////sequence_ = source.read_4_bytes_little_endian();
228
    ////size_ = serialized_size(*script_, *witness_);
229
    ////valid_ = source;
UNCOV
230
}
×
231

232
// Serialization.
233
// ----------------------------------------------------------------------------
234

235
data_chunk input::to_data() const NOEXCEPT
1✔
236
{
237
    data_chunk data(serialized_size(false));
1✔
238
    stream::out::copy ostream(data);
1✔
239
    to_data(ostream);
1✔
240
    return data;
2✔
241
}
1✔
242

243
void input::to_data(std::ostream& stream) const NOEXCEPT
2✔
244
{
245
    write::bytes::ostream out(stream);
2✔
246
    to_data(out);
2✔
247
}
2✔
248

249
// Witness is serialized by transaction.
250
void input::to_data(writer& sink) const NOEXCEPT
1,392✔
251
{
252
    point_->to_data(sink);
1,392✔
253
    script_->to_data(sink, true);
1,392✔
254
    sink.write_4_bytes_little_endian(sequence_);
1,392✔
255
}
1,392✔
256

257
// static/private
258
input::sizes input::serialized_size(const chain::script& script,
1,186✔
259
    const chain::witness& witness) NOEXCEPT
260
{
261
    constexpr auto const_size = ceilinged_add(
1,186✔
262
        point::serialized_size(),
263
        sizeof(sequence_));
264

265
    const auto nominal_size = ceilinged_add(
1,186✔
266
        const_size,
267
        script.serialized_size(true));
268

269
    const auto witnessed_size = ceilinged_add(
1,186✔
270
        nominal_size,
271
        witness.serialized_size(true));
272

273
    return { nominal_size, witnessed_size };
1,186✔
274
}
275

276
// input.serialized_size(witness) provides sizing for witness, however
277
// witnesses are serialized by the transaction. This is an ugly hack as a
278
// consequence of bip144 not serializing witnesses as part of inputs, which
279
// is logically the proper association.
280
size_t input::serialized_size(bool witness) const NOEXCEPT
2✔
281
{
282
    return witness ? size_.witnessed : size_.nominal;
2✔
283
}
284

285
// Friend accessors (private).
286
// ----------------------------------------------------------------------------
287

288
size_t input::nominal_size() const NOEXCEPT
2,130✔
289
{
290
    return size_.nominal;
2,130✔
291
}
292

293
size_t input::witnessed_size() const NOEXCEPT
46✔
294
{
295
    return size_.witnessed;
46✔
296
}
297

298
////void input::set_witness(reader& source) NOEXCEPT
299
////{
300
////    witness_ = to_shared<chain::witness>(source, true);
301
////    size_.witnessed = ceilinged_add(size_.nominal,
302
////        witness_->serialized_size(true));
303
////}
304

305
void input::set_witness(reader& source) NOEXCEPT
20✔
306
{
307
    auto& allocator = source.get_allocator();
20✔
308

309
    witness_.reset(
20✔
310
        allocator.new_object<chain::witness>(source, true),
20✔
311
        allocator.deleter<chain::witness>(source.get_arena()));
20✔
312

313
    size_.witnessed = ceilinged_add(size_.nominal,
20✔
314
        witness_->serialized_size(true));
315
}
20✔
316

317
// Properties.
318
// ----------------------------------------------------------------------------
319

320
bool input::is_valid() const NOEXCEPT
16✔
321
{
322
    return valid_;
16✔
323
}
324

325
const point& input::point() const NOEXCEPT
169✔
326
{
327
    return *point_;
10✔
328
}
329

330
const chain::script& input::script() const NOEXCEPT
75✔
331
{
332
    return *script_;
10✔
333
}
334

335
const chain::witness& input::witness() const NOEXCEPT
1,893✔
336
{
337
    return *witness_;
10✔
338
}
339

340
const point::cptr& input::point_ptr() const NOEXCEPT
×
341
{
342
    return point_;
×
343
}
344

345
const chain::script::cptr& input::script_ptr() const NOEXCEPT
1,555✔
346
{
347
    return script_;
1,555✔
348
}
349

350
const chain::witness::cptr& input::witness_ptr() const NOEXCEPT
×
351
{
352
    return witness_;
×
353
}
354

355
uint32_t input::sequence() const NOEXCEPT
73✔
356
{
357
    return sequence_;
10✔
358
}
359

360
// Methods.
361
// ----------------------------------------------------------------------------
362

363
bool input::is_final() const NOEXCEPT
22✔
364
{
365
    return sequence_ == max_input_sequence;
22✔
366
}
367

368
bool input::is_roller() const NOEXCEPT
4✔
369
{
370
    return script_->is_roller() || (prevout && prevout->script().is_roller());
4✔
371
}
372

373
// static
374
bool input::is_locked(uint32_t sequence, size_t height,
10✔
375
    uint32_t median_time_past, size_t prevout_height,
376
    uint32_t prevout_median_time_past) NOEXCEPT
377
{
378
    // BIP68: if bit 31 is set then no consensus meaning is applied.
379
    if (get_right(sequence, relative_locktime_disabled_bit))
10✔
380
        return false;
381

382
    // BIP68: the low 16 bits of the sequence apply to relative lock-time.
383
    const auto blocks = mask_left(sequence, relative_locktime_mask_left);
8✔
384

385
    // BIP68: bit 22 determines if relative lock is time or block based.
386
    if (get_right(sequence, relative_locktime_time_locked_bit))
8✔
387
    {
388
        // BIP68: change sequence to seconds by shift up by 9 bits (x 512).
389
        auto time = shift_left(blocks, relative_locktime_seconds_shift_left);
3✔
390
        auto age = floored_subtract(median_time_past, prevout_median_time_past);
3✔
391
        return age < time;
3✔
392
    }
393

394
    const auto age = floored_subtract(height, prevout_height);
5✔
395
    return age < blocks;
5✔
396
}
397

398
bool input::is_locked(size_t height, uint32_t median_time_past) const NOEXCEPT
10✔
399
{
400
    // Prevout must be found and height/median_time_past metadata populated.
401
    ////BC_ASSERT(!is_zero(metadata.height));
402
    return is_locked(sequence_, height, median_time_past, metadata.height,
10✔
403
        metadata.median_time_past);
10✔
404
}
405

406
bool input::reserved_hash(hash_digest& out) const NOEXCEPT
×
407
{
408
    if (!witness::is_reserved_pattern(witness_->stack()))
×
409
        return false;
410

411
    std::copy_n(witness_->stack().front()->begin(), hash_size, out.begin());
×
412
    return true;
×
413
}
414

415
// private
416
// prevout_script is only used to determine is_pay_script_hash_pattern.
417
bool input::extract_sigop_script(chain::script& out,
×
418
    const chain::script& prevout_script) const NOEXCEPT
419
{
420
    // There are no embedded sigops when the prevout script is not p2sh.
421
    if (!script::is_pay_script_hash_pattern(prevout_script.ops()))
×
422
        return false;
423

424
    // There are no embedded sigops when the input script is not push only.
425
    const auto& ops = script_->ops();
×
426
    if (ops.empty() || !script::is_relaxed_push_pattern(ops))
×
427
        return false;
×
428

429
    // Parse the embedded script from the last input script item (data).
430
    // This cannot fail because there is no prefix to invalidate the length.
431
    out = { ops.back().data(), false };
×
432
    return true;
×
433
}
434

435
// TODO: Prior to block 79400 sigops were limited only by policy.
436
// TODO: Create legacy sigops fork/flag and pass here, return 0 if false.
437
// TODO: this was an unbipped flag day soft fork, prior to BIP16/141.
438
// TODO: if (nHeight > 79400 && GetSigOpCount() > MAX_BLOCK_SIGOPS).
439
size_t input::signature_operations(bool bip16, bool bip141) const NOEXCEPT
7✔
440
{
441
    // Penalize quadratic signature operations (bip141).
442
    const auto factor = bip141 ? heavy_sigops_factor : one;
7✔
443
    const auto sigops = script_->signature_operations(false) * factor;
7✔
444

445
    // ************************************************************************
446
    // CONSENSUS: coinbase input cannot execute, but sigops are counted anyway.
447
    // ************************************************************************
448
    if (!prevout)
7✔
449
        return sigops;
450

451
    // Null prevout/input (coinbase) cannot have witness or embedded script.
452
    // Embedded/witness scripts are deserialized here and again on scipt eval.
453

454
    chain::script witness;
×
455
    if (bip141 && witness_->extract_sigop_script(witness, prevout->script()))
×
456
    {
457
        // Add sigops in the witness script (bip141).
458
        return ceilinged_add(sigops, witness.signature_operations(true));
×
459
    }
460

461
    chain::script embedded;
×
462
    if (bip16 && extract_sigop_script(embedded, prevout->script()))
×
463
    {
464
        if (bip141 && witness_->extract_sigop_script(witness, embedded))
×
465
        {
466
            // Add sigops in the embedded witness script (bip141).
467
            return ceilinged_add(sigops, witness.signature_operations(true));
×
468
        }
469
        else
470
        {
471
            // Add heavy sigops in the embedded script (bip16).
472
            return ceilinged_add(sigops, embedded.signature_operations(true) *
×
473
                factor);
474
        }
475
    }
476

477
    return sigops;
478
}
×
479

480
BC_POP_WARNING()
481

482
// JSON value convertors.
483
// ----------------------------------------------------------------------------
484

485
namespace json = boost::json;
486

487
// boost/json will soon have NOEXCEPT: github.com/boostorg/json/pull/636
488
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
489

490
input tag_invoke(json::value_to_tag<input>, const json::value& value) NOEXCEPT
5✔
491
{
492
    return
5✔
493
    {
494
        json::value_to<chain::point>(value.at("point")),
5✔
495
        json::value_to<chain::script>(value.at("script")),
5✔
496
        json::value_to<chain::witness>(value.at("witness")),
10✔
497
        value.at("sequence").to_number<uint32_t>()
10✔
498
    };
5✔
499
}
500

501
void tag_invoke(json::value_from_tag, json::value& value,
10✔
502
    const input& input) NOEXCEPT
503
{
504
    value =
10✔
505
    {
506
        { "point", input.point() },
507
        { "script", input.script() },
508
        { "witness", input.witness() },
509
        { "sequence", input.sequence() }
510
    };
10✔
511
}
10✔
512

513
BC_POP_WARNING()
514

515
input::cptr tag_invoke(json::value_to_tag<input::cptr>,
×
516
    const json::value& value) NOEXCEPT
517
{
518
    return to_shared(tag_invoke(json::value_to_tag<input>{}, value));
×
519
}
520

521
// Shared pointer overload is required for navigation.
522
BC_PUSH_WARNING(SMART_PTR_NOT_NEEDED)
523
BC_PUSH_WARNING(NO_VALUE_OR_CONST_REF_SHARED_PTR)
524

525
void tag_invoke(json::value_from_tag tag, json::value& value,
8✔
526
    const input::cptr& input) NOEXCEPT
527
{
528
    tag_invoke(tag, value, *input);
8✔
529
}
8✔
530

531
BC_POP_WARNING()
532
BC_POP_WARNING()
533

534
} // namespace chain
535
} // namespace system
536
} // 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