/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 | } |