/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 | 0 | { |
20 | 0 | switch (t) { Branch (20:13): [True: 0, False: 0]
|
21 | 0 | case TxoutType::NONSTANDARD: return "nonstandard"; Branch (21:5): [True: 0, False: 0]
|
22 | 0 | case TxoutType::PUBKEY: return "pubkey"; Branch (22:5): [True: 0, False: 0]
|
23 | 0 | case TxoutType::PUBKEYHASH: return "pubkeyhash"; Branch (23:5): [True: 0, False: 0]
|
24 | 0 | case TxoutType::SCRIPTHASH: return "scripthash"; Branch (24:5): [True: 0, False: 0]
|
25 | 0 | case TxoutType::MULTISIG: return "multisig"; Branch (25:5): [True: 0, False: 0]
|
26 | 0 | case TxoutType::NULL_DATA: return "nulldata"; Branch (26:5): [True: 0, False: 0]
|
27 | 0 | case TxoutType::ANCHOR: return "anchor"; Branch (27:5): [True: 0, False: 0]
|
28 | 0 | case TxoutType::WITNESS_V0_KEYHASH: return "witness_v0_keyhash"; Branch (28:5): [True: 0, False: 0]
|
29 | 0 | case TxoutType::WITNESS_V0_SCRIPTHASH: return "witness_v0_scripthash"; Branch (29:5): [True: 0, False: 0]
|
30 | 0 | case TxoutType::WITNESS_V1_TAPROOT: return "witness_v1_taproot"; Branch (30:5): [True: 0, False: 0]
|
31 | 0 | case TxoutType::WITNESS_UNKNOWN: return "witness_unknown"; Branch (31:5): [True: 0, False: 0]
|
32 | 0 | } // no default case, so the compiler can warn about missing cases |
33 | 0 | assert(false); Branch (33:5): [Folded - Ignored]
|
34 | 0 | } |
35 | | |
36 | | static bool MatchPayToPubkey(const CScript& script, valtype& pubkey) |
37 | 0 | { |
38 | 0 | if (script.size() == CPubKey::SIZE + 2 && script[0] == CPubKey::SIZE && script.back() == OP_CHECKSIG) { Branch (38:9): [True: 0, False: 0]
Branch (38:47): [True: 0, False: 0]
Branch (38:77): [True: 0, False: 0]
|
39 | 0 | pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::SIZE + 1); |
40 | 0 | return CPubKey::ValidSize(pubkey); |
41 | 0 | } |
42 | 0 | if (script.size() == CPubKey::COMPRESSED_SIZE + 2 && script[0] == CPubKey::COMPRESSED_SIZE && script.back() == OP_CHECKSIG) { Branch (42:9): [True: 0, False: 0]
Branch (42:58): [True: 0, False: 0]
Branch (42:99): [True: 0, False: 0]
|
43 | 0 | pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_SIZE + 1); |
44 | 0 | return CPubKey::ValidSize(pubkey); |
45 | 0 | } |
46 | 0 | return false; |
47 | 0 | } |
48 | | |
49 | | static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash) |
50 | 0 | { |
51 | 0 | 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: 0, False: 0]
Branch (51:32): [True: 0, False: 0]
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 | 0 | return false; |
56 | 0 | } |
57 | | |
58 | | /** Test for "small positive integer" script opcodes - OP_1 through OP_16. */ |
59 | | static constexpr bool IsSmallInteger(opcodetype opcode) |
60 | 0 | { |
61 | 0 | return opcode >= OP_1 && opcode <= OP_16; Branch (61:12): [True: 0, False: 0]
Branch (61:30): [True: 0, False: 0]
|
62 | 0 | } |
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 | 0 | { |
68 | 0 | int count; |
69 | 0 | if (IsSmallInteger(opcode)) { Branch (69:9): [True: 0, False: 0]
|
70 | 0 | count = CScript::DecodeOP_N(opcode); |
71 | 0 | } else if (IsPushdataOp(opcode)) { Branch (71:16): [True: 0, False: 0]
|
72 | 0 | if (!CheckMinimalPush(data, opcode)) return {}; Branch (72:13): [True: 0, False: 0]
|
73 | 0 | try { |
74 | 0 | count = CScriptNum(data, /* fRequireMinimal = */ true).getint(); |
75 | 0 | } catch (const scriptnum_error&) { |
76 | 0 | return {}; |
77 | 0 | } |
78 | 0 | } else { |
79 | 0 | return {}; |
80 | 0 | } |
81 | 0 | if (count < min || count > max) return {}; Branch (81:9): [True: 0, False: 0]
Branch (81:24): [True: 0, False: 0]
|
82 | 0 | return count; |
83 | 0 | } |
84 | | |
85 | | static bool MatchMultisig(const CScript& script, int& required_sigs, std::vector<valtype>& pubkeys) |
86 | 0 | { |
87 | 0 | opcodetype opcode; |
88 | 0 | valtype data; |
89 | |
|
90 | 0 | CScript::const_iterator it = script.begin(); |
91 | 0 | if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false; Branch (91:9): [True: 0, False: 0]
Branch (91:30): [True: 0, False: 0]
|
92 | | |
93 | 0 | if (!script.GetOp(it, opcode, data)) return false; Branch (93:9): [True: 0, False: 0]
|
94 | 0 | auto req_sigs = GetScriptNumber(opcode, data, 1, MAX_PUBKEYS_PER_MULTISIG); |
95 | 0 | if (!req_sigs) return false; Branch (95:9): [True: 0, False: 0]
|
96 | 0 | required_sigs = *req_sigs; |
97 | 0 | while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) { Branch (97:12): [True: 0, False: 0]
Branch (97:46): [True: 0, False: 0]
|
98 | 0 | pubkeys.emplace_back(std::move(data)); |
99 | 0 | } |
100 | 0 | auto num_keys = GetScriptNumber(opcode, data, required_sigs, MAX_PUBKEYS_PER_MULTISIG); |
101 | 0 | if (!num_keys) return false; Branch (101:9): [True: 0, False: 0]
|
102 | 0 | if (pubkeys.size() != static_cast<unsigned long>(*num_keys)) return false; Branch (102:9): [True: 0, False: 0]
|
103 | | |
104 | 0 | return (it + 1 == script.end()); |
105 | 0 | } |
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 | 0 | { |
143 | 0 | 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 | 0 | if (scriptPubKey.IsPayToScriptHash()) Branch (147:9): [True: 0, False: 0]
|
148 | 0 | { |
149 | 0 | std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22); |
150 | 0 | vSolutionsRet.push_back(hashBytes); |
151 | 0 | return TxoutType::SCRIPTHASH; |
152 | 0 | } |
153 | | |
154 | 0 | int witnessversion; |
155 | 0 | std::vector<unsigned char> witnessprogram; |
156 | 0 | if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) { Branch (156:9): [True: 0, False: 0]
|
157 | 0 | if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) { Branch (157:13): [True: 0, False: 0]
Branch (157:36): [True: 0, False: 0]
|
158 | 0 | vSolutionsRet.push_back(std::move(witnessprogram)); |
159 | 0 | return TxoutType::WITNESS_V0_KEYHASH; |
160 | 0 | } |
161 | 0 | if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) { Branch (161:13): [True: 0, False: 0]
Branch (161:36): [True: 0, False: 0]
|
162 | 0 | vSolutionsRet.push_back(std::move(witnessprogram)); |
163 | 0 | return TxoutType::WITNESS_V0_SCRIPTHASH; |
164 | 0 | } |
165 | 0 | if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) { Branch (165:13): [True: 0, False: 0]
Branch (165:36): [True: 0, False: 0]
|
166 | 0 | vSolutionsRet.push_back(std::move(witnessprogram)); |
167 | 0 | return TxoutType::WITNESS_V1_TAPROOT; |
168 | 0 | } |
169 | 0 | if (scriptPubKey.IsPayToAnchor()) { Branch (169:13): [True: 0, False: 0]
|
170 | 0 | return TxoutType::ANCHOR; |
171 | 0 | } |
172 | 0 | if (witnessversion != 0) { Branch (172:13): [True: 0, False: 0]
|
173 | 0 | vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion}); |
174 | 0 | vSolutionsRet.push_back(std::move(witnessprogram)); |
175 | 0 | return TxoutType::WITNESS_UNKNOWN; |
176 | 0 | } |
177 | 0 | return TxoutType::NONSTANDARD; |
178 | 0 | } |
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 | 0 | if (scriptPubKey.size() >= 1 && scriptPubKey[0] == OP_RETURN && scriptPubKey.IsPushOnly(scriptPubKey.begin()+1)) { Branch (185:9): [True: 0, False: 0]
Branch (185:9): [True: 0, False: 0]
Branch (185:37): [True: 0, False: 0]
Branch (185:69): [True: 0, False: 0]
|
186 | 0 | return TxoutType::NULL_DATA; |
187 | 0 | } |
188 | | |
189 | 0 | std::vector<unsigned char> data; |
190 | 0 | if (MatchPayToPubkey(scriptPubKey, data)) { Branch (190:9): [True: 0, False: 0]
|
191 | 0 | vSolutionsRet.push_back(std::move(data)); |
192 | 0 | return TxoutType::PUBKEY; |
193 | 0 | } |
194 | | |
195 | 0 | if (MatchPayToPubkeyHash(scriptPubKey, data)) { Branch (195:9): [True: 0, False: 0]
|
196 | 0 | vSolutionsRet.push_back(std::move(data)); |
197 | 0 | return TxoutType::PUBKEYHASH; |
198 | 0 | } |
199 | | |
200 | 0 | int required; |
201 | 0 | std::vector<std::vector<unsigned char>> keys; |
202 | 0 | if (MatchMultisig(scriptPubKey, required, keys)) { Branch (202:9): [True: 0, False: 0]
|
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 | 0 | vSolutionsRet.clear(); |
210 | 0 | return TxoutType::NONSTANDARD; |
211 | 0 | } |
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 | } |