Coverage Report

Created: 2025-06-10 13:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/bitcoin/src/script/solver.cpp
Line
Count
Source
1
// Copyright (c) 2009-2010 Satoshi Nakamoto
2
// Copyright (c) 2009-present The Bitcoin Core developers
3
// Distributed under the MIT software license, see the accompanying
4
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6
#include <pubkey.h>
7
#include <script/interpreter.h>
8
#include <script/script.h>
9
#include <script/solver.h>
10
#include <span.h>
11
12
#include <algorithm>
13
#include <cassert>
14
#include <string>
15
16
typedef std::vector<unsigned char> valtype;
17
18
std::string GetTxnOutputType(TxoutType t)
19
1.58M
{
20
1.58M
    switch (t) {
  Branch (20:13): [True: 0, False: 1.58M]
21
144k
    case TxoutType::NONSTANDARD: return "nonstandard";
  Branch (21:5): [True: 144k, False: 1.44M]
22
144k
    case TxoutType::PUBKEY: return "pubkey";
  Branch (22:5): [True: 144k, False: 1.44M]
23
144k
    case TxoutType::PUBKEYHASH: return "pubkeyhash";
  Branch (23:5): [True: 144k, False: 1.44M]
24
144k
    case TxoutType::SCRIPTHASH: return "scripthash";
  Branch (24:5): [True: 144k, False: 1.44M]
25
144k
    case TxoutType::MULTISIG: return "multisig";
  Branch (25:5): [True: 144k, False: 1.44M]
26
144k
    case TxoutType::NULL_DATA: return "nulldata";
  Branch (26:5): [True: 144k, False: 1.44M]
27
144k
    case TxoutType::ANCHOR: return "anchor";
  Branch (27:5): [True: 144k, False: 1.44M]
28
144k
    case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
  Branch (28:5): [True: 144k, False: 1.44M]
29
144k
    case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
  Branch (29:5): [True: 144k, False: 1.44M]
30
144k
    case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
  Branch (30:5): [True: 144k, False: 1.44M]
31
144k
    case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
  Branch (31:5): [True: 144k, False: 1.44M]
32
1.58M
    } // no default case, so the compiler can warn about missing cases
33
1.58M
    assert(false);
  Branch (33:5): [Folded - Ignored]
34
0
}
35
36
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
37
198
{
38
198
    if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) {
  Branch (38:9): [True: 3, False: 195]
  Branch (38:47): [True: 1, False: 2]
  Branch (38:77): [True: 0, False: 1]
39
0
        pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1);
40
0
        return CPubKey::ValidSize(pubkey);
41
0
    }
42
198
    if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) {
  Branch (42:9): [True: 119, False: 79]
  Branch (42:58): [True: 4, False: 115]
  Branch (42:99): [True: 1, False: 3]
43
1
        pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1);
44
1
        return CPubKey::ValidSize(pubkey);
45
1
    }
46
197
    return false;
47
198
}
48
49
static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
50
198
{
51
198
    if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
  Branch (51:9): [True: 3, False: 195]
  Branch (51:32): [True: 0, False: 3]
  Branch (51:55): [True: 0, False: 0]
  Branch (51:82): [True: 0, False: 0]
  Branch (51:101): [True: 0, False: 0]
  Branch (51:133): [True: 0, False: 0]
52
0
        pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
53
0
        return true;
54
0
    }
55
198
    return false;
56
198
}
57
58
/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
59
static constexpr bool IsSmallInteger(opcodetype opcode)
60
177
{
61
177
    return opcode >= OP_1 && opcode <= OP_16;
  Branch (61:12): [True: 96, False: 81]
  Branch (61:30): [True: 79, False: 17]
62
177
}
63
64
/** Retrieve a minimally-encoded number in range [min,max] from an (opcode, data) pair,
65
 *  whether it's OP_n or through a push. */
66
static std::optional<int> GetScriptNumber(opcodetype opcode, valtype data, int min, int max)
67
177
{
68
177
    int count;
69
177
    if (IsSmallInteger(opcode)) {
  Branch (69:9): [True: 79, False: 98]
70
79
        count = CScript::DecodeOP_N(opcode);
71
98
    } else if (IsPushdataOp(opcode)) {
  Branch (71:16): [True: 78, False: 20]
72
78
        if (!CheckMinimalPush(data, opcode)) return {};
  Branch (72:13): [True: 30, False: 48]
73
48
        try {
74
48
            count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
75
48
        } catch (const scriptnum_error&) {
76
20
            return {};
77
20
        }
78
48
    } else {
79
20
        return {};
80
20
    }
81
107
    if (count < min || count > max) return {};
  Branch (81:9): [True: 7, False: 100]
  Branch (81:24): [True: 13, False: 87]
82
87
    return count;
83
107
}
84
85
static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
86
198
{
87
198
    opcodetype opcode;
88
198
    valtype data;
89
90
198
    CScript::const_iterator it = script.begin();
91
198
    if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
  Branch (91:9): [True: 10, False: 188]
  Branch (91:30): [True: 78, False: 110]
92
93
110
    if (!script.GetOp(it, opcode, data)) return false;
  Branch (93:9): [True: 6, False: 104]
94
104
    auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);
95
104
    if (!req_sigs) return false;
  Branch (95:9): [True: 31, False: 73]
96
73
    required_sigs = *req_sigs;
97
73
    while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
  Branch (97:12): [True: 62, False: 11]
  Branch (97:46): [True: 0, False: 62]
98
0
        pubkeys.emplace_back(std::move(data));
99
0
    }
100
73
    auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);
101
73
    if (!num_keys) return false;
  Branch (101:9): [True: 59, False: 14]
102
14
    if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;
  Branch (102:9): [True: 14, False: 0]
103
104
0
    return (it + 1 == script.end());
105
14
}
106
107
std::optional<std::pair<int, std::vector<std::span<const unsigned char>>>> MatchMultiA(const CScript& script)
108
0
{
109
0
    std::vector<std::span<const unsigned char>> keyspans;
110
111
    // Redundant, but very fast and selective test.
112
0
    if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {};
  Branch (112:9): [True: 0, False: 0]
  Branch (112:31): [True: 0, False: 0]
  Branch (112:50): [True: 0, False: 0]
113
114
    // Parse keys
115
0
    auto it = script.begin();
116
0
    while (script.end() - it >= 34) {
  Branch (116:12): [True: 0, False: 0]
117
0
        if (*it != 32) return {};
  Branch (117:13): [True: 0, False: 0]
118
0
        ++it;
119
0
        keyspans.emplace_back(&*it, 32);
120
0
        it += 32;
121
0
        if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {};
  Branch (121:13): [True: 0, False: 0]
  Branch (121:21): [True: 0, False: 0]
122
0
        ++it;
123
0
    }
124
0
    if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};
  Branch (124:9): [True: 0, False: 0]
  Branch (124:33): [True: 0, False: 0]
125
126
    // Parse threshold.
127
0
    opcodetype opcode;
128
0
    std::vector<unsigned char> data;
129
0
    if (!script.GetOp(it, opcode, data)) return {};
  Branch (129:9): [True: 0, False: 0]
130
0
    if (it == script.end()) return {};
  Branch (130:9): [True: 0, False: 0]
131
0
    if (*it != OP_NUMEQUAL) return {};
  Branch (131:9): [True: 0, False: 0]
132
0
    ++it;
133
0
    if (it != script.end()) return {};
  Branch (133:9): [True: 0, False: 0]
134
0
    auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());
135
0
    if (!threshold) return {};
  Branch (135:9): [True: 0, False: 0]
136
137
    // Construct result.
138
0
    return std::pair{*threshold, std::move(keyspans)};
139
0
}
140
141
TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
142
522k
{
143
522k
    vSolutionsRet.clear();
144
145
    // Shortcut for pay-to-script-hash, which are more constrained than the other types:
146
    // it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
147
522k
    if (scriptPubKey.IsPayToScriptHash())
  Branch (147:9): [True: 16.3k, False: 506k]
148
16.3k
    {
149
16.3k
        std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
150
16.3k
        vSolutionsRet.push_back(hashBytes);
151
16.3k
        return TxoutType::SCRIPTHASH;
152
16.3k
    }
153
154
506k
    int witnessversion;
155
506k
    std::vector<unsigned char> witnessprogram;
156
506k
    if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
  Branch (156:9): [True: 461k, False: 44.2k]
157
461k
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
  Branch (157:13): [True: 423k, False: 38.1k]
  Branch (157:36): [True: 0, False: 423k]
158
0
            vSolutionsRet.push_back(std::move(witnessprogram));
159
0
            return TxoutType::WITNESS_V0_KEYHASH;
160
0
        }
161
461k
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
  Branch (161:13): [True: 423k, False: 38.1k]
  Branch (161:36): [True: 423k, False: 5]
162
423k
            vSolutionsRet.push_back(std::move(witnessprogram));
163
423k
            return TxoutType::WITNESS_V0_SCRIPTHASH;
164
423k
        }
165
38.1k
        if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
  Branch (165:13): [True: 38.1k, False: 15]
  Branch (165:36): [True: 0, False: 38.1k]
166
0
            vSolutionsRet.push_back(std::move(witnessprogram));
167
0
            return TxoutType::WITNESS_V1_TAPROOT;
168
0
        }
169
38.1k
        if (scriptPubKey.IsPayToAnchor()) {
  Branch (169:13): [True: 38.1k, False: 20]
170
38.1k
            return TxoutType::ANCHOR;
171
38.1k
        }
172
20
        if (witnessversion != 0) {
  Branch (172:13): [True: 15, False: 5]
173
15
            vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
174
15
            vSolutionsRet.push_back(std::move(witnessprogram));
175
15
            return TxoutType::WITNESS_UNKNOWN;
176
15
        }
177
5
        return TxoutType::NONSTANDARD;
178
20
    }
179
180
    // Provably prunable, data-carrying output
181
    //
182
    // So long as script passes the IsUnspendable() test and all but the first
183
    // byte passes the IsPushOnly() test we don't care what exactly is in the
184
    // script.
185
44.2k
    if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
  Branch (185:9): [True: 44.2k, False: 10]
  Branch (185:9): [True: 44.0k, False: 198]
  Branch (185:37): [True: 44.0k, False: 165]
  Branch (185:69): [True: 44.0k, False: 23]
186
44.0k
        return TxoutType::NULL_DATA;
187
44.0k
    }
188
189
198
    std::vector<unsigned char> data;
190
198
    if (MatchPayToPubkey(scriptPubKey, data)) {
  Branch (190:9): [True: 0, False: 198]
191
0
        vSolutionsRet.push_back(std::move(data));
192
0
        return TxoutType::PUBKEY;
193
0
    }
194
195
198
    if (MatchPayToPubkeyHash(scriptPubKey, data)) {
  Branch (195:9): [True: 0, False: 198]
196
0
        vSolutionsRet.push_back(std::move(data));
197
0
        return TxoutType::PUBKEYHASH;
198
0
    }
199
200
198
    int required;
201
198
    std::vector<std::vector<unsigned char>> keys;
202
198
    if (MatchMultisig(scriptPubKey, required, keys)) {
  Branch (202:9): [True: 0, False: 198]
203
0
        vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
204
0
        vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
205
0
        vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
206
0
        return TxoutType::MULTISIG;
207
0
    }
208
209
198
    vSolutionsRet.clear();
210
198
    return TxoutType::NONSTANDARD;
211
198
}
212
213
CScript GetScriptForRawPubKey(const CPubKey& pubKey)
214
0
{
215
0
    return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG;
216
0
}
217
218
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
219
0
{
220
0
    CScript script;
221
222
0
    script << nRequired;
223
0
    for (const CPubKey& key : keys)
  Branch (223:29): [True: 0, False: 0]
224
0
        script << ToByteVector(key);
225
0
    script << keys.size() << OP_CHECKMULTISIG;
226
227
0
    return script;
228
0
}