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

libbitcoin / libbitcoin-system / 14957620207

11 May 2025 04:23PM UTC coverage: 82.287% (-0.2%) from 82.469%
14957620207

push

github

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

Refactor sig operations, reintroduce multisighash caching.

54 of 124 new or added lines in 8 files covered. (43.55%)

9 existing lines in 6 files now uncovered.

10299 of 12516 relevant lines covered (82.29%)

3849629.69 hits per line

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

74.44
/src/chain/transaction_sign.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/transaction.hpp>
20

21
#include <iterator>
22
#include <bitcoin/system/chain/enums/coverage.hpp>
23
#include <bitcoin/system/chain/input.hpp>
24
#include <bitcoin/system/chain/output.hpp>
25
#include <bitcoin/system/chain/script.hpp>
26
#include <bitcoin/system/data/data.hpp>
27
#include <bitcoin/system/endian/endian.hpp>
28
#include <bitcoin/system/define.hpp>
29
#include <bitcoin/system/error/error.hpp>
30
#include <bitcoin/system/hash/hash.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
// All private methods pertaining to the validation of a signature.
41
// There are three versions of signature hashing and verification implemented.
42
// Version: (unversioned) original, (0) bip143/segwit, (1) bip341/taproot.
43

44
// Share fixed elements of signature hashing.
45
// ----------------------------------------------------------------------------
46

47
constexpr auto prefixed = true;
48

49
static const auto& null_output() NOEXCEPT
1✔
50
{
51
    static const auto null = output{}.to_data();
1✔
52
    return null;
1✔
53
}
54

55
static const auto& empty_script() NOEXCEPT
8✔
56
{
57
    static const auto empty = script{}.to_data(prefixed);
8✔
58
    return empty;
8✔
59
}
60

61
static const auto& zero_sequence() NOEXCEPT
1✔
62
{
63
    static const auto sequence = to_little_endian<uint32_t>(0);
1✔
64
    return sequence;
1✔
65
}
66

67
// Compute signature hash subcomponents.
68
// ----------------------------------------------------------------------------
69

70
hash_digest transaction::outputs_hash() const NOEXCEPT
8✔
71
{
72
    if (sighash_cache_)
8✔
73
        return sighash_cache_->outputs;
×
74

75
    hash_digest digest{};
8✔
76
    stream::out::fast stream{ digest };
8✔
77
    hash::sha256x2::fast sink{ stream };
8✔
78

79
    for (const auto& output: *outputs_)
22✔
80
        output->to_data(sink);
14✔
81

82
    sink.flush();
8✔
83
    return digest;
8✔
84
}
8✔
85

86
hash_digest transaction::points_hash() const NOEXCEPT
11✔
87
{
88
    if (sighash_cache_)
11✔
89
        return sighash_cache_->points;
×
90

91
    hash_digest digest{};
11✔
92
    stream::out::fast stream{ digest };
11✔
93
    hash::sha256x2::fast sink{ stream };
11✔
94

95
    for (const auto& input: *inputs_)
27✔
96
        input->point().to_data(sink);
16✔
97

98
    sink.flush();
11✔
99
    return digest;
11✔
100
}
11✔
101

102
hash_digest transaction::sequences_hash() const NOEXCEPT
7✔
103
{
104
    if (sighash_cache_)
7✔
105
        return sighash_cache_->sequences;
×
106

107
    hash_digest digest{};
7✔
108
    stream::out::fast stream{ digest };
7✔
109
    hash::sha256x2::fast sink{ stream };
7✔
110

111
    for (const auto& input: *inputs_)
17✔
112
        sink.write_4_bytes_little_endian(input->sequence());
10✔
113

114
    sink.flush();
7✔
115
    return digest;
7✔
116
}
7✔
117

118
// Signature hashing (unversioned).
119
// ----------------------------------------------------------------------------
120

121
transaction::input_iterator transaction::input_at(
4✔
122
    uint32_t index) const NOEXCEPT
123
{
124
    // Guarded by check_signature and create_endorsement.
125
    BC_ASSERT_MSG(index < inputs_->size(), "invalid input index");
4✔
126

127
    return std::next(inputs_->begin(), index);
4✔
128
}
129

130
uint32_t transaction::input_index(const input_iterator& input) const NOEXCEPT
25✔
131
{
132
    // Guarded by unversioned_sighash and output_hash.
133
    BC_ASSERT_MSG(inputs_->begin() != inputs_->end(), "invalid input iterator");
25✔
134

135
    return possible_narrow_and_sign_cast<uint32_t>(
25✔
136
        std::distance(inputs_->begin(), input));
×
137
}
138

139
// static
140
//*****************************************************************************
141
// CONSENSUS: Due to masking of bits 6/7 (8 is the anyone_can_pay flag),
142
// there are 4 possible 7 bit values that can set "single" and 4 others that
143
// can set none, and yet all other values set "all".
144
//*****************************************************************************
145
inline coverage transaction::mask_sighash(uint8_t sighash_flags) NOEXCEPT
41✔
146
{
147
    switch (bit_and<uint8_t>(sighash_flags, coverage::mask))
41✔
148
    {
149
        case coverage::hash_single:
150
            return coverage::hash_single;
151
        case coverage::hash_none:
2✔
152
            return coverage::hash_none;
2✔
153
        default:
18✔
154
            return coverage::hash_all;
18✔
155
    }
156
}
157

158
inline bool transaction::is_anyone_can_pay(uint8_t sighash_flags) NOEXCEPT
159
{
160
    return to_bool(bit_and<uint8_t>(sighash_flags, coverage::anyone_can_pay));
161
}
162

163
// ****************************************************************************
164
// CONSENSUS: sighash flags are carried in a single byte but are encoded as 4
165
// bytes in the signature hash preimage serialization.
166
// ****************************************************************************
167

168
void transaction::signature_hash_single(writer& sink,
4✔
169
    const input_iterator& input, const script& subscript,
170
    uint8_t sighash_flags) const NOEXCEPT
171
{
172
    const auto write_inputs = [this, &input, &subscript, sighash_flags](
8✔
173
        writer& sink) NOEXCEPT
174
    {
175
        input_cptrs::const_iterator in;
4✔
176
        const auto anyone = is_anyone_can_pay(sighash_flags);
4✔
177

178
        sink.write_variable(anyone ? one : inputs_->size());
4✔
179

180
        for (in = inputs_->begin(); !anyone && in != input; ++in)
4✔
181
        {
182
            (*in)->point().to_data(sink);
×
183
            sink.write_bytes(empty_script());
×
184
            sink.write_bytes(zero_sequence());
×
185
        }
186

187
        (*input)->point().to_data(sink);
4✔
188
        subscript.to_data(sink, prefixed);
4✔
189
        sink.write_4_bytes_little_endian((*input)->sequence());
4✔
190

191
        for (++in; !anyone && in != inputs_->end(); ++in)
5✔
192
        {
193
            (*in)->point().to_data(sink);
1✔
194
            sink.write_bytes(empty_script());
1✔
195
            sink.write_bytes(zero_sequence());
1✔
196
        }
197
    };
4✔
198

199
    const auto write_outputs = [this, &input](writer& sink) NOEXCEPT
8✔
200
    {
201
        const auto index = input_index(input);
4✔
202

203
        sink.write_variable(add1(index));
4✔
204

205
        for (size_t output = 0; output < index; ++output)
5✔
206
            sink.write_bytes(null_output());
1✔
207

208
        // Guarded by unversioned_sighash.
209
        outputs_->at(index)->to_data(sink);
4✔
210
    };
8✔
211

212
    sink.write_4_bytes_little_endian(version_);
4✔
213
    write_inputs(sink);
4✔
214
    write_outputs(sink);
4✔
215
    sink.write_4_bytes_little_endian(locktime_);
4✔
216
    sink.write_4_bytes_little_endian(sighash_flags);
4✔
217
}
4✔
218

219
void transaction::signature_hash_none(writer& sink,
×
220
    const input_iterator& input, const script& subscript,
221
    uint8_t sighash_flags) const NOEXCEPT
222
{
223
    const auto write_inputs = [this, &input, &subscript, sighash_flags](
×
224
        writer& sink) NOEXCEPT
225
    {
226
        input_cptrs::const_iterator in;
×
NEW
227
        const auto anyone = is_anyone_can_pay(sighash_flags);
×
228

229
        sink.write_variable(anyone ? one : inputs_->size());
×
230

231
        for (in = inputs_->begin(); !anyone && in != input; ++in)
×
232
        {
233
            (*in)->point().to_data(sink);
×
234
            sink.write_bytes(empty_script());
×
235
            sink.write_bytes(zero_sequence());
×
236
        }
237

238
        (*input)->point().to_data(sink);
×
239
        subscript.to_data(sink, prefixed);
×
240
        sink.write_4_bytes_little_endian((*input)->sequence());
×
241

242
        for (++in; !anyone && in != inputs_->end(); ++in)
×
243
        {
244
            (*in)->point().to_data(sink);
×
245
            sink.write_bytes(empty_script());
×
246
            sink.write_bytes(zero_sequence());
×
247
        }
248
    };
×
249

250
    sink.write_4_bytes_little_endian(version_);
×
251
    write_inputs(sink);
×
252
    sink.write_variable(zero);
×
253
    sink.write_4_bytes_little_endian(locktime_);
×
254
    sink.write_4_bytes_little_endian(sighash_flags);
×
255
}
×
256

257
void transaction::signature_hash_all(writer& sink,
12✔
258
    const input_iterator& input, const script& subscript,
259
    uint8_t sighash_flags) const NOEXCEPT
260
{
261
    const auto write_inputs = [this, &input, &subscript, sighash_flags](
24✔
262
        writer& sink) NOEXCEPT
263
    {
264
        input_cptrs::const_iterator in;
12✔
265
        const auto anyone = is_anyone_can_pay(sighash_flags);
12✔
266

267
        sink.write_variable(anyone ? one : inputs_->size());
12✔
268

269
        for (in = inputs_->begin(); !anyone && in != input; ++in)
15✔
270
        {
271
            (*in)->point().to_data(sink);
3✔
272
            sink.write_bytes(empty_script());
3✔
273
            sink.write_4_bytes_little_endian((*in)->sequence());
3✔
274
        }
275

276
        (*input)->point().to_data(sink);
12✔
277
        subscript.to_data(sink, prefixed);
12✔
278
        sink.write_4_bytes_little_endian((*input)->sequence());
12✔
279

280
        for (++in; !anyone && in != inputs_->end(); ++in)
16✔
281
        {
282
            (*in)->point().to_data(sink);
4✔
283
            sink.write_bytes(empty_script());
4✔
284
            sink.write_4_bytes_little_endian((*in)->sequence());
4✔
285
        }
286
    };
12✔
287

288
    const auto write_outputs = [this](writer& sink) NOEXCEPT
24✔
289
    {
290
        sink.write_variable(outputs_->size());
12✔
291
        for (const auto& output: *outputs_)
27✔
292
            output->to_data(sink);
15✔
293
    };
24✔
294

295
    sink.write_4_bytes_little_endian(version_);
12✔
296
    write_inputs(sink);
12✔
297
    write_outputs(sink);
12✔
298
    sink.write_4_bytes_little_endian(locktime_);
12✔
299
    sink.write_4_bytes_little_endian(sighash_flags);
12✔
300
}
12✔
301

302
// private
303
hash_digest transaction::unversioned_sighash(
19✔
304
    const input_iterator& input, const script& subscript,
305
    uint8_t sighash_flags) const NOEXCEPT
306
{
307
    // Set options.
308
    const auto flag = mask_sighash(sighash_flags);
19✔
309

310
    // Create hash writer.
311
    hash_digest digest{};
19✔
312
    stream::out::fast stream{ digest };
19✔
313
    hash::sha256x2::fast sink{ stream };
19✔
314

315
    switch (flag)
19✔
316
    {
317
        case coverage::hash_single:
7✔
318
        {
7✔
319
            //*****************************************************************
320
            // CONSENSUS: return one_hash if index exceeds outputs in sighash.
321
            // Related Bug: bitcointalk.org/index.php?topic=260595
322
            // Exploit: joncave.co.uk/2014/08/bitcoin-sighash-single/
323
            //*****************************************************************
324
            if (input_index(input) >= outputs_->size())
7✔
325
                return one_hash;
3✔
326

327
            signature_hash_single(sink, input, subscript, sighash_flags);
4✔
328
            break;
4✔
329
        }
330
        case coverage::hash_none:
×
331
        {
×
332
            signature_hash_none(sink, input, subscript, sighash_flags);
×
333
            break;
×
334
        }
335
        default:
12✔
336
        case coverage::hash_all:
12✔
337
        {
12✔
338
            signature_hash_all(sink, input, subscript, sighash_flags);
12✔
339
        }
340
    }
341

342
    sink.flush();
16✔
343
    return digest;
16✔
344
}
19✔
345

346
// Signature hashing (version 0 - segwit).
347
// ----------------------------------------------------------------------------
348

349
// TODO: taproot requires both single and double hash of each.
350
void transaction::initialize_sighash_cache() const NOEXCEPT
2✔
351
{
352
    if (!segregated_)
2✔
353
        return;
354

355
    // This overconstructs the cache (anyone or !all), however it is simple.
356
    sighash_cache_ =
2✔
357
    {
358
        outputs_hash(),
2✔
359
        points_hash(),
2✔
360
        sequences_hash()
2✔
361
    };
2✔
362
}
363

364
hash_digest transaction::output_hash(
14✔
365
    const input_iterator& input) const NOEXCEPT
366
{
367
    const auto index = input_index(input);
14✔
368

369
    //*************************************************************************
370
    // CONSENSUS: if index exceeds outputs in signature hash, return null_hash.
371
    //*************************************************************************
372
    if (index >= outputs_->size())
14✔
373
        return null_hash;
2✔
374

375
    hash_digest digest{};
12✔
376
    stream::out::fast stream{ digest };
12✔
377
    hash::sha256x2::fast sink{ stream };
12✔
378
    outputs_->at(index)->to_data(sink);
12✔
379
    sink.flush();
12✔
380
    return digest;
12✔
381
}
12✔
382

383
hash_digest transaction::version_0_sighash(const input_iterator& input,
22✔
384
    const script& subscript, uint64_t value,
385
    uint8_t sighash_flags) const NOEXCEPT
386
{
387
    // Set options.
388
    const auto anyone = is_anyone_can_pay(sighash_flags);
22✔
389
    const auto flag = mask_sighash(sighash_flags);
22✔
390
    const auto all = (flag == coverage::hash_all);
22✔
391
    const auto single = (flag == coverage::hash_single);
22✔
392

393
    // Create hash writer.
394
    hash_digest digest{};
22✔
395
    stream::out::fast stream{ digest };
22✔
396
    hash::sha256x2::fast sink{ stream };
22✔
397

398
    // Create signature hash.
399
    sink.write_little_endian(version_);
22✔
400

401
    // Conditioning points, sequences, and outputs writes on cache_ instead of
402
    // conditionally passing them from methods avoids copying the cached hash.
403

404
    // points
405
    sink.write_bytes(!anyone ? points_hash() : null_hash);
22✔
406

407
    // sequences
408
    sink.write_bytes(!anyone && all ? sequences_hash() : null_hash);
22✔
409

410
    (*input)->point().to_data(sink);
22✔
411
    subscript.to_data(sink, prefixed);
22✔
412
    sink.write_little_endian(value);
22✔
413
    sink.write_little_endian((*input)->sequence());
22✔
414

415
    // outputs
416
    if (single)
22✔
417
        sink.write_bytes(output_hash(input));
14✔
418
    else
419
        sink.write_bytes(all ? outputs_hash() : null_hash);
8✔
420

421
    sink.write_little_endian(locktime_);
22✔
422
    sink.write_4_bytes_little_endian(sighash_flags);
22✔
423

424
    sink.flush();
22✔
425
    return digest;
44✔
426
}
22✔
427

428
// Signature hashing (version 1 - taproot).
429
// ----------------------------------------------------------------------------
430

431
// TODO: tapscript.
432
// Because the codeseparator_position is the last input to the hash, the SHA256
433
// midstate can be efficiently cached for multiple OP_CODESEPARATOR in a script.
434

UNCOV
435
hash_digest transaction::version_1_sighash(const input_iterator& input,
×
436
    const script& script, uint64_t value, uint8_t sighash_flags) const NOEXCEPT
437
{
438
    // Set options.
NEW
439
    const auto anyone = is_anyone_can_pay(sighash_flags);
×
NEW
440
    const auto flag = mask_sighash(sighash_flags);
×
NEW
441
    const auto all = (flag == coverage::hash_all);
×
NEW
442
    const auto single = (flag == coverage::hash_single);
×
443

444
    // Create hash writer.
NEW
445
    hash_digest digest{};
×
NEW
446
    stream::out::fast stream{ digest };
×
NEW
447
    hash::sha256x2::fast sink{ stream };
×
448

449
    // Create signature hash.
NEW
450
    sink.write_little_endian(version_);
×
451

452
    // Conditioning points, sequences, and outputs writes on cache_ instead of
453
    // conditionally passing them from methods avoids copying the cached hash.
454

455
    // points
NEW
456
    sink.write_bytes(!anyone ? points_hash() : null_hash);
×
457

458
    // sequences
NEW
459
    sink.write_bytes(!anyone && all ? sequences_hash() : null_hash);
×
460

NEW
461
    (*input)->point().to_data(sink);
×
NEW
462
    script.to_data(sink, prefixed);
×
NEW
463
    sink.write_little_endian(value);
×
NEW
464
    sink.write_little_endian((*input)->sequence());
×
465

466
    // outputs
NEW
467
    if (single)
×
NEW
468
        sink.write_bytes(output_hash(input));
×
469
    else
NEW
470
        sink.write_bytes(all ? outputs_hash() : null_hash);
×
471

NEW
472
    sink.write_little_endian(locktime_);
×
NEW
473
    sink.write_4_bytes_little_endian(sighash_flags);
×
474

NEW
475
    sink.flush();
×
NEW
476
    return digest;
×
UNCOV
477
}
×
478

479
} // namespace chain
480
} // namespace system
481
} // 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