Coverage Report

Created: 2026-05-06 07:53

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
366k
{
20
366k
    switch (t) {
21
31.7k
    case TxoutType::NONSTANDARD: return "nonstandard";
22
31.9k
    case TxoutType::PUBKEY: return "pubkey";
23
32.0k
    case TxoutType::PUBKEYHASH: return "pubkeyhash";
24
32.0k
    case TxoutType::SCRIPTHASH: return "scripthash";
25
31.7k
    case TxoutType::MULTISIG: return "multisig";
26
33.5k
    case TxoutType::NULL_DATA: return "nulldata";
27
31.6k
    case TxoutType::ANCHOR: return "anchor";
28
33.3k
    case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
29
31.8k
    case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
30
44.9k
    case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
31
31.6k
    case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
32
366k
    } // no default case, so the compiler can warn about missing cases
33
366k
    assert(false);
34
0
}
35
36
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
37
339k
{
38
339k
    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
339k
    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
318k
    return false;
47
339k
}
48
49
static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
50
318k
{
51
318k
    if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
52
312k
        pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
53
312k
        return true;
54
312k
    }
55
5.98k
    return false;
56
318k
}
57
58
/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
59
static constexpr bool IsSmallInteger(opcodetype opcode)
60
3.68k
{
61
3.68k
    return opcode >= OP_1 && opcode <= OP_16;
62
3.68k
}
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.68k
{
68
3.68k
    int count;
69
3.68k
    if (IsSmallInteger(opcode)) {
70
3.55k
        count = CScript::DecodeOP_N(opcode);
71
3.55k
    } 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.66k
    if (count < min || count > max) return {};
82
3.66k
    return count;
83
3.66k
}
84
85
static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
86
5.98k
{
87
5.98k
    opcodetype opcode;
88
5.98k
    valtype data;
89
90
5.98k
    CScript::const_iterator it = script.begin();
91
5.98k
    if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
92
93
1.46k
    if (!script.GetOp(it, opcode, data)) return false;
94
1.46k
    auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);
95
1.46k
    if (!req_sigs) return false;
96
1.44k
    required_sigs = *req_sigs;
97
7.27k
    while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
98
5.83k
        pubkeys.emplace_back(std::move(data));
99
5.83k
    }
100
1.44k
    auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);
101
1.44k
    if (!num_keys) return false;
102
1.44k
    if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;
103
104
1.43k
    return (it + 1 == script.end());
105
1.44k
}
106
107
std::optional<std::pair<int, std::vector<std::span<const unsigned char>>>> MatchMultiA(const CScript& script)
108
1.03k
{
109
1.03k
    std::vector<std::span<const unsigned char>> keyspans;
110
111
    // Redundant, but very fast and selective test.
112
1.03k
    if (script.size() == 0 || script[0] != 32 || script.back() != OP_NUMEQUAL) return {};
113
114
    // Parse keys
115
778
    auto it = script.begin();
116
108k
    while (script.end() - it >= 34) {
117
107k
        if (*it != 32) return {};
118
107k
        ++it;
119
107k
        keyspans.emplace_back(&*it, 32);
120
107k
        it += 32;
121
107k
        if (*it != (keyspans.size() == 1 ? OP_CHECKSIG : OP_CHECKSIGADD)) return {};
122
107k
        ++it;
123
107k
    }
124
778
    if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};
125
126
    // Parse threshold.
127
778
    opcodetype opcode;
128
778
    std::vector<unsigned char> data;
129
778
    if (!script.GetOp(it, opcode, data)) return {};
130
778
    if (it == script.end()) return {};
131
778
    if (*it != OP_NUMEQUAL) return {};
132
778
    ++it;
133
778
    if (it != script.end()) return {};
134
778
    auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());
135
778
    if (!threshold) return {};
136
137
    // Construct result.
138
778
    return std::pair{*threshold, std::move(keyspans)};
139
778
}
140
141
TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
142
1.10M
{
143
1.10M
    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.10M
    if (scriptPubKey.IsPayToScriptHash())
148
87.3k
    {
149
87.3k
        std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
150
87.3k
        vSolutionsRet.push_back(hashBytes);
151
87.3k
        return TxoutType::SCRIPTHASH;
152
87.3k
    }
153
154
1.01M
    int witnessversion;
155
1.01M
    std::vector<unsigned char> witnessprogram;
156
1.01M
    if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
157
666k
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
158
508k
            vSolutionsRet.push_back(std::move(witnessprogram));
159
508k
            return TxoutType::WITNESS_V0_KEYHASH;
160
508k
        }
161
158k
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
162
5.26k
            vSolutionsRet.push_back(std::move(witnessprogram));
163
5.26k
            return TxoutType::WITNESS_V0_SCRIPTHASH;
164
5.26k
        }
165
153k
        if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
166
152k
            vSolutionsRet.push_back(std::move(witnessprogram));
167
152k
            return TxoutType::WITNESS_V1_TAPROOT;
168
152k
        }
169
453
        if (scriptPubKey.IsPayToAnchor()) {
170
108
            return TxoutType::ANCHOR;
171
108
        }
172
345
        if (witnessversion != 0) {
173
344
            vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
174
344
            vSolutionsRet.push_back(std::move(witnessprogram));
175
344
            return TxoutType::WITNESS_UNKNOWN;
176
344
        }
177
1
        return TxoutType::NONSTANDARD;
178
345
    }
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
351k
    if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
186
12.2k
        return TxoutType::NULL_DATA;
187
12.2k
    }
188
189
339k
    std::vector<unsigned char> data;
190
339k
    if (MatchPayToPubkey(scriptPubKey, data)) {
191
20.8k
        vSolutionsRet.push_back(std::move(data));
192
20.8k
        return TxoutType::PUBKEY;
193
20.8k
    }
194
195
318k
    if (MatchPayToPubkeyHash(scriptPubKey, data)) {
196
312k
        vSolutionsRet.push_back(std::move(data));
197
312k
        return TxoutType::PUBKEYHASH;
198
312k
    }
199
200
5.98k
    int required;
201
5.98k
    std::vector<std::vector<unsigned char>> keys;
202
5.98k
    if (MatchMultisig(scriptPubKey, required, keys)) {
203
1.43k
        vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
204
1.43k
        vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
205
1.43k
        vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
206
1.43k
        return TxoutType::MULTISIG;
207
1.43k
    }
208
209
4.54k
    vSolutionsRet.clear();
210
4.54k
    return TxoutType::NONSTANDARD;
211
5.98k
}
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
}