Coverage Report

Created: 2026-04-29 19:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/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
355k
{
20
355k
    switch (t) {
21
30.7k
    case TxoutType::NONSTANDARD: return "nonstandard";
22
30.9k
    case TxoutType::PUBKEY: return "pubkey";
23
31.1k
    case TxoutType::PUBKEYHASH: return "pubkeyhash";
24
31.1k
    case TxoutType::SCRIPTHASH: return "scripthash";
25
30.7k
    case TxoutType::MULTISIG: return "multisig";
26
32.5k
    case TxoutType::NULL_DATA: return "nulldata";
27
30.7k
    case TxoutType::ANCHOR: return "anchor";
28
32.2k
    case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
29
30.8k
    case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
30
43.5k
    case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
31
30.7k
    case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
32
355k
    } // no default case, so the compiler can warn about missing cases
33
355k
    assert(false);
34
0
}
35
36
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
37
340k
{
38
340k
    if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) {
39
161
        pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1);
40
161
        return CPubKey::ValidSize(pubkey);
41
161
    }
42
340k
    if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) {
43
20.7k
        pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1);
44
20.7k
        return CPubKey::ValidSize(pubkey);
45
20.7k
    }
46
319k
    return false;
47
340k
}
48
49
static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
50
319k
{
51
319k
    if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
52
313k
        pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
53
313k
        return true;
54
313k
    }
55
5.95k
    return false;
56
319k
}
57
58
/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
59
static constexpr bool IsSmallInteger(opcodetype opcode)
60
3.73k
{
61
3.73k
    return opcode >= OP_1 && opcode <= OP_16;
62
3.73k
}
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
3.73k
{
68
3.73k
    int count;
69
3.73k
    if (IsSmallInteger(opcode)) {
70
3.59k
        count = CScript::DecodeOP_N(opcode);
71
3.59k
    } else if (IsPushdataOp(opcode)) {
72
120
        if (!CheckMinimalPush(data, opcode)) return {};
73
116
        try {
74
116
            count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
75
116
        } catch (const scriptnum_error&) {
76
0
            return {};
77
0
        }
78
116
    } else {
79
16
        return {};
80
16
    }
81
3.71k
    if (count < min || count > max) return {};
82
3.70k
    return count;
83
3.71k
}
84
85
static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
86
5.95k
{
87
5.95k
    opcodetype opcode;
88
5.95k
    valtype data;
89
90
5.95k
    CScript::const_iterator it = script.begin();
91
5.95k
    if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
92
93
1.47k
    if (!script.GetOp(it, opcode, data)) return false;
94
1.47k
    auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);
95
1.47k
    if (!req_sigs) return false;
96
1.46k
    required_sigs = *req_sigs;
97
7.33k
    while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
98
5.87k
        pubkeys.emplace_back(std::move(data));
99
5.87k
    }
100
1.46k
    auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);
101
1.46k
    if (!num_keys) return false;
102
1.45k
    if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;
103
104
1.45k
    return (it + 1 == script.end());
105
1.45k
}
106
107
std::optional<std::pair<int, std::vector<std::span<const unsigned char>>>> MatchMultiA(const CScript& script)
108
1.04k
{
109
1.04k
    std::vector<std::span<const unsigned char>> keyspans;
110
111
    // Redundant, but very fast and selective test.
112
1.04k
    if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {};
113
114
    // Parse keys
115
790
    auto it = script.begin();
116
110k
    while (script.end() - it >= 34) {
117
109k
        if (*it != 32) return {};
118
109k
        ++it;
119
109k
        keyspans.emplace_back(&*it, 32);
120
109k
        it += 32;
121
109k
        if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {};
122
109k
        ++it;
123
109k
    }
124
790
    if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};
125
126
    // Parse threshold.
127
790
    opcodetype opcode;
128
790
    std::vector<unsigned char> data;
129
790
    if (!script.GetOp(it, opcode, data)) return {};
130
790
    if (it == script.end()) return {};
131
790
    if (*it != OP_NUMEQUAL) return {};
132
790
    ++it;
133
790
    if (it != script.end()) return {};
134
790
    auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());
135
790
    if (!threshold) return {};
136
137
    // Construct result.
138
790
    return std::pair{*threshold, std::move(keyspans)};
139
790
}
140
141
TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
142
1.11M
{
143
1.11M
    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
1.11M
    if (scriptPubKey.IsPayToScriptHash())
148
89.6k
    {
149
89.6k
        std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
150
89.6k
        vSolutionsRet.push_back(hashBytes);
151
89.6k
        return TxoutType::SCRIPTHASH;
152
89.6k
    }
153
154
1.02M
    int witnessversion;
155
1.02M
    std::vector<unsigned char> witnessprogram;
156
1.02M
    if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
157
667k
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
158
504k
            vSolutionsRet.push_back(std::move(witnessprogram));
159
504k
            return TxoutType::WITNESS_V0_KEYHASH;
160
504k
        }
161
163k
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
162
5.30k
            vSolutionsRet.push_back(std::move(witnessprogram));
163
5.30k
            return TxoutType::WITNESS_V0_SCRIPTHASH;
164
5.30k
        }
165
158k
        if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
166
158k
            vSolutionsRet.push_back(std::move(witnessprogram));
167
158k
            return TxoutType::WITNESS_V1_TAPROOT;
168
158k
        }
169
449
        if (scriptPubKey.IsPayToAnchor()) {
170
108
            return TxoutType::ANCHOR;
171
108
        }
172
341
        if (witnessversion != 0) {
173
340
            vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
174
340
            vSolutionsRet.push_back(std::move(witnessprogram));
175
340
            return TxoutType::WITNESS_UNKNOWN;
176
340
        }
177
1
        return TxoutType::NONSTANDARD;
178
341
    }
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
352k
    if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
186
12.3k
        return TxoutType::NULL_DATA;
187
12.3k
    }
188
189
340k
    std::vector<unsigned char> data;
190
340k
    if (MatchPayToPubkey(scriptPubKey, data)) {
191
20.8k
        vSolutionsRet.push_back(std::move(data));
192
20.8k
        return TxoutType::PUBKEY;
193
20.8k
    }
194
195
319k
    if (MatchPayToPubkeyHash(scriptPubKey, data)) {
196
313k
        vSolutionsRet.push_back(std::move(data));
197
313k
        return TxoutType::PUBKEYHASH;
198
313k
    }
199
200
5.95k
    int required;
201
5.95k
    std::vector<std::vector<unsigned char>> keys;
202
5.95k
    if (MatchMultisig(scriptPubKey, required, keys)) {
203
1.45k
        vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
204
1.45k
        vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
205
1.45k
        vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
206
1.45k
        return TxoutType::MULTISIG;
207
1.45k
    }
208
209
4.50k
    vSolutionsRet.clear();
210
4.50k
    return TxoutType::NONSTANDARD;
211
5.95k
}
212
213
CScript GetScriptForRawPubKey(const CPubKey& pubKey)
214
19.9k
{
215
19.9k
    return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG;
216
19.9k
}
217
218
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
219
18.7k
{
220
18.7k
    CScript script;
221
222
18.7k
    script << nRequired;
223
18.7k
    for (const CPubKey& key : keys)
224
114k
        script << ToByteVector(key);
225
18.7k
    script << keys.size() << OP_CHECKMULTISIG;
226
227
18.7k
    return script;
228
18.7k
}