Coverage Report

Created: 2025-10-29 16:48

/root/bitcoin/src/script/solver.cpp
Line
Count
Source (jump to first uncovered line)
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
0
{
20
0
    switch (t) {
21
0
    case TxoutType::NONSTANDARD: return "nonstandard";
22
0
    case TxoutType::PUBKEY: return "pubkey";
23
0
    case TxoutType::PUBKEYHASH: return "pubkeyhash";
24
0
    case TxoutType::SCRIPTHASH: return "scripthash";
25
0
    case TxoutType::MULTISIG: return "multisig";
26
0
    case TxoutType::NULL_DATA: return "nulldata";
27
0
    case TxoutType::ANCHOR: return "anchor";
28
0
    case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash";
29
0
    case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash";
30
0
    case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot";
31
0
    case TxoutType::WITNESS_UNKNOWN: return "witness_unknown";
32
0
    } // no default case, so the compiler can warn about missing cases
33
0
    assert(false);
34
0
}
35
36
static bool MatchPayToPubkey(const CScript& script, valtype& pubkey)
37
114k
{
38
114k
    if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) {
39
1.12k
        pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1);
40
1.12k
        return CPubKey::ValidSize(pubkey);
41
1.12k
    }
42
113k
    if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) {
43
7.59k
        pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1);
44
7.59k
        return CPubKey::ValidSize(pubkey);
45
7.59k
    }
46
105k
    return false;
47
113k
}
48
49
static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash)
50
105k
{
51
105k
    if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) {
52
33.3k
        pubkeyhash = valtype(script.begin () + 3, script.begin() + 23);
53
33.3k
        return true;
54
33.3k
    }
55
72.3k
    return false;
56
105k
}
57
58
/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */
59
static constexpr bool IsSmallInteger(opcodetype opcode)
60
128k
{
61
128k
    return opcode >= OP_1 && opcode <= OP_16;
62
128k
}
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
128k
{
68
128k
    int count;
69
128k
    if (IsSmallInteger(opcode)) {
70
128k
        count = CScript::DecodeOP_N(opcode);
71
128k
    } else if (IsPushdataOp(opcode)) {
72
200
        if (!CheckMinimalPush(data, opcode)) return {};
73
190
        try {
74
190
            count = CScriptNum(data, /* fRequireMinimal = */ true).getint();
75
190
        } catch (const scriptnum_error&) {
76
130
            return {};
77
130
        }
78
190
    } else {
79
95
        return {};
80
95
    }
81
128k
    if (count < min || count > max) return {};
82
128k
    return count;
83
128k
}
84
85
static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys)
86
72.3k
{
87
72.3k
    opcodetype opcode;
88
72.3k
    valtype data;
89
90
72.3k
    CScript::const_iterator it = script.begin();
91
72.3k
    if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false;
92
93
64.4k
    if (!script.GetOp(it, opcode, data)) return false;
94
64.3k
    auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG);
95
64.3k
    if (!req_sigs) return false;
96
64.1k
    required_sigs = *req_sigs;
97
500k
    while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) {
98
436k
        pubkeys.emplace_back(std::move(data));
99
436k
    }
100
64.1k
    auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG);
101
64.1k
    if (!num_keys) return false;
102
64.0k
    if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false;
103
104
64.0k
    return (it + 1 == script.end());
105
64.0k
}
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 {};
113
114
    // Parse keys
115
0
    auto it = script.begin();
116
0
    while (script.end() - it >= 34) {
117
0
        if (*it != 32) return {};
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 {};
122
0
        ++it;
123
0
    }
124
0
    if (keyspans.size() == 0 || keyspans.size() > MAX_PUBKEYS_PER_MULTI_A) return {};
125
126
    // Parse threshold.
127
0
    opcodetype opcode;
128
0
    std::vector<unsigned char> data;
129
0
    if (!script.GetOp(it, opcode, data)) return {};
130
0
    if (it == script.end()) return {};
131
0
    if (*it != OP_NUMEQUAL) return {};
132
0
    ++it;
133
0
    if (it != script.end()) return {};
134
0
    auto threshold = GetScriptNumber(opcode, data, 1, (int)keyspans.size());
135
0
    if (!threshold) return {};
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
416k
{
143
416k
    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
416k
    if (scriptPubKey.IsPayToScriptHash())
148
173k
    {
149
173k
        std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
150
173k
        vSolutionsRet.push_back(hashBytes);
151
173k
        return TxoutType::SCRIPTHASH;
152
173k
    }
153
154
243k
    int witnessversion;
155
243k
    std::vector<unsigned char> witnessprogram;
156
243k
    if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
157
128k
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
158
37.3k
            vSolutionsRet.push_back(std::move(witnessprogram));
159
37.3k
            return TxoutType::WITNESS_V0_KEYHASH;
160
37.3k
        }
161
91.3k
        if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
162
91.2k
            vSolutionsRet.push_back(std::move(witnessprogram));
163
91.2k
            return TxoutType::WITNESS_V0_SCRIPTHASH;
164
91.2k
        }
165
90
        if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
166
0
            vSolutionsRet.push_back(std::move(witnessprogram));
167
0
            return TxoutType::WITNESS_V1_TAPROOT;
168
0
        }
169
90
        if (scriptPubKey.IsPayToAnchor()) {
170
0
            return TxoutType::ANCHOR;
171
0
        }
172
90
        if (witnessversion != 0) {
173
55
            vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
174
55
            vSolutionsRet.push_back(std::move(witnessprogram));
175
55
            return TxoutType::WITNESS_UNKNOWN;
176
55
        }
177
35
        return TxoutType::NONSTANDARD;
178
90
    }
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
114k
    if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) {
186
75
        return TxoutType::NULL_DATA;
187
75
    }
188
189
114k
    std::vector<unsigned char> data;
190
114k
    if (MatchPayToPubkey(scriptPubKey, data)) {
191
8.67k
        vSolutionsRet.push_back(std::move(data));
192
8.67k
        return TxoutType::PUBKEY;
193
8.67k
    }
194
195
105k
    if (MatchPayToPubkeyHash(scriptPubKey, data)) {
196
33.3k
        vSolutionsRet.push_back(std::move(data));
197
33.3k
        return TxoutType::PUBKEYHASH;
198
33.3k
    }
199
200
72.3k
    int required;
201
72.3k
    std::vector<std::vector<unsigned char>> keys;
202
72.3k
    if (MatchMultisig(scriptPubKey, required, keys)) {
203
64.0k
        vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
204
64.0k
        vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
205
64.0k
        vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
206
64.0k
        return TxoutType::MULTISIG;
207
64.0k
    }
208
209
8.37k
    vSolutionsRet.clear();
210
8.37k
    return TxoutType::NONSTANDARD;
211
72.3k
}
212
213
CScript GetScriptForRawPubKey(const CPubKey& pubKey)
214
12.9k
{
215
12.9k
    return CScript() << std::vector<unsigned char>(pubKey.begin(), pubKey.end()) << OP_CHECKSIG;
216
12.9k
}
217
218
CScript GetScriptForMultisig(int nRequired, const std::vector<CPubKey>& keys)
219
27.8k
{
220
27.8k
    CScript script;
221
222
27.8k
    script << nRequired;
223
27.8k
    for (const CPubKey& key : keys)
224
187k
        script << ToByteVector(key);
225
27.8k
    script << keys.size() << OP_CHECKMULTISIG;
226
227
27.8k
    return script;
228
27.8k
}