/root/bitcoin/src/core_read.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | // Copyright (c) 2009-2022 The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #include <core_io.h> |
6 | | |
7 | | #include <primitives/block.h> |
8 | | #include <primitives/transaction.h> |
9 | | #include <script/script.h> |
10 | | #include <script/sign.h> |
11 | | #include <serialize.h> |
12 | | #include <streams.h> |
13 | | #include <util/result.h> |
14 | | #include <util/strencodings.h> |
15 | | |
16 | | #include <algorithm> |
17 | | #include <string> |
18 | | |
19 | | using util::SplitString; |
20 | | |
21 | | namespace { |
22 | | class OpCodeParser |
23 | | { |
24 | | private: |
25 | | std::map<std::string, opcodetype> mapOpNames; |
26 | | |
27 | | public: |
28 | | OpCodeParser() |
29 | 0 | { |
30 | 0 | for (unsigned int op = 0; op <= MAX_OPCODE; ++op) { |
31 | | // Allow OP_RESERVED to get into mapOpNames |
32 | 0 | if (op < OP_NOP && op != OP_RESERVED) { |
33 | 0 | continue; |
34 | 0 | } |
35 | | |
36 | 0 | std::string strName = GetOpName(static_cast<opcodetype>(op)); |
37 | 0 | if (strName == "OP_UNKNOWN") { |
38 | 0 | continue; |
39 | 0 | } |
40 | 0 | mapOpNames[strName] = static_cast<opcodetype>(op); |
41 | | // Convenience: OP_ADD and just ADD are both recognized: |
42 | 0 | if (strName.compare(0, 3, "OP_") == 0) { // strName starts with "OP_" |
43 | 0 | mapOpNames[strName.substr(3)] = static_cast<opcodetype>(op); |
44 | 0 | } |
45 | 0 | } |
46 | 0 | } |
47 | | opcodetype Parse(const std::string& s) const |
48 | 0 | { |
49 | 0 | auto it = mapOpNames.find(s); |
50 | 0 | if (it == mapOpNames.end()) throw std::runtime_error("script parse error: unknown opcode"); |
51 | 0 | return it->second; |
52 | 0 | } |
53 | | }; |
54 | | |
55 | | opcodetype ParseOpCode(const std::string& s) |
56 | 0 | { |
57 | 0 | static const OpCodeParser ocp; |
58 | 0 | return ocp.Parse(s); |
59 | 0 | } |
60 | | |
61 | | } // namespace |
62 | | |
63 | | CScript ParseScript(const std::string& s) |
64 | 0 | { |
65 | 0 | CScript result; |
66 | |
|
67 | 0 | std::vector<std::string> words = SplitString(s, " \t\n"); |
68 | |
|
69 | 0 | for (const std::string& w : words) { |
70 | 0 | if (w.empty()) { |
71 | | // Empty string, ignore. (SplitString doesn't combine multiple separators) |
72 | 0 | } else if (std::all_of(w.begin(), w.end(), ::IsDigit) || |
73 | 0 | (w.front() == '-' && w.size() > 1 && std::all_of(w.begin() + 1, w.end(), ::IsDigit))) |
74 | 0 | { |
75 | | // Number |
76 | 0 | const auto num{ToIntegral<int64_t>(w)}; |
77 | | |
78 | | // limit the range of numbers ParseScript accepts in decimal |
79 | | // since numbers outside -0xFFFFFFFF...0xFFFFFFFF are illegal in scripts |
80 | 0 | if (!num.has_value() || num > int64_t{0xffffffff} || num < -1 * int64_t{0xffffffff}) { |
81 | 0 | throw std::runtime_error("script parse error: decimal numeric value only allowed in the " |
82 | 0 | "range -0xFFFFFFFF...0xFFFFFFFF"); |
83 | 0 | } |
84 | | |
85 | 0 | result << num.value(); |
86 | 0 | } else if (w.substr(0, 2) == "0x" && w.size() > 2 && IsHex(std::string(w.begin() + 2, w.end()))) { |
87 | | // Raw hex data, inserted NOT pushed onto stack: |
88 | 0 | std::vector<unsigned char> raw = ParseHex(std::string(w.begin() + 2, w.end())); |
89 | 0 | result.insert(result.end(), raw.begin(), raw.end()); |
90 | 0 | } else if (w.size() >= 2 && w.front() == '\'' && w.back() == '\'') { |
91 | | // Single-quoted string, pushed as data. NOTE: this is poor-man's |
92 | | // parsing, spaces/tabs/newlines in single-quoted strings won't work. |
93 | 0 | std::vector<unsigned char> value(w.begin() + 1, w.end() - 1); |
94 | 0 | result << value; |
95 | 0 | } else { |
96 | | // opcode, e.g. OP_ADD or ADD: |
97 | 0 | result << ParseOpCode(w); |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | 0 | return result; |
102 | 0 | } |
103 | | |
104 | | // Check that all of the input and output scripts of a transaction contains valid opcodes |
105 | | static bool CheckTxScriptsSanity(const CMutableTransaction& tx) |
106 | 0 | { |
107 | | // Check input scripts for non-coinbase txs |
108 | 0 | if (!CTransaction(tx).IsCoinBase()) { |
109 | 0 | for (unsigned int i = 0; i < tx.vin.size(); i++) { |
110 | 0 | if (!tx.vin[i].scriptSig.HasValidOps() || tx.vin[i].scriptSig.size() > MAX_SCRIPT_SIZE) { |
111 | 0 | return false; |
112 | 0 | } |
113 | 0 | } |
114 | 0 | } |
115 | | // Check output scripts |
116 | 0 | for (unsigned int i = 0; i < tx.vout.size(); i++) { |
117 | 0 | if (!tx.vout[i].scriptPubKey.HasValidOps() || tx.vout[i].scriptPubKey.size() > MAX_SCRIPT_SIZE) { |
118 | 0 | return false; |
119 | 0 | } |
120 | 0 | } |
121 | | |
122 | 0 | return true; |
123 | 0 | } |
124 | | |
125 | | static bool DecodeTx(CMutableTransaction& tx, const std::vector<unsigned char>& tx_data, bool try_no_witness, bool try_witness) |
126 | 0 | { |
127 | | // General strategy: |
128 | | // - Decode both with extended serialization (which interprets the 0x0001 tag as a marker for |
129 | | // the presence of witnesses) and with legacy serialization (which interprets the tag as a |
130 | | // 0-input 1-output incomplete transaction). |
131 | | // - Restricted by try_no_witness (which disables legacy if false) and try_witness (which |
132 | | // disables extended if false). |
133 | | // - Ignore serializations that do not fully consume the hex string. |
134 | | // - If neither succeeds, fail. |
135 | | // - If only one succeeds, return that one. |
136 | | // - If both decode attempts succeed: |
137 | | // - If only one passes the CheckTxScriptsSanity check, return that one. |
138 | | // - If neither or both pass CheckTxScriptsSanity, return the extended one. |
139 | |
|
140 | 0 | CMutableTransaction tx_extended, tx_legacy; |
141 | 0 | bool ok_extended = false, ok_legacy = false; |
142 | | |
143 | | // Try decoding with extended serialization support, and remember if the result successfully |
144 | | // consumes the entire input. |
145 | 0 | if (try_witness) { |
146 | 0 | DataStream ssData(tx_data); |
147 | 0 | try { |
148 | 0 | ssData >> TX_WITH_WITNESS(tx_extended); |
149 | 0 | if (ssData.empty()) ok_extended = true; |
150 | 0 | } catch (const std::exception&) { |
151 | | // Fall through. |
152 | 0 | } |
153 | 0 | } |
154 | | |
155 | | // Optimization: if extended decoding succeeded and the result passes CheckTxScriptsSanity, |
156 | | // don't bother decoding the other way. |
157 | 0 | if (ok_extended && CheckTxScriptsSanity(tx_extended)) { |
158 | 0 | tx = std::move(tx_extended); |
159 | 0 | return true; |
160 | 0 | } |
161 | | |
162 | | // Try decoding with legacy serialization, and remember if the result successfully consumes the entire input. |
163 | 0 | if (try_no_witness) { |
164 | 0 | DataStream ssData(tx_data); |
165 | 0 | try { |
166 | 0 | ssData >> TX_NO_WITNESS(tx_legacy); |
167 | 0 | if (ssData.empty()) ok_legacy = true; |
168 | 0 | } catch (const std::exception&) { |
169 | | // Fall through. |
170 | 0 | } |
171 | 0 | } |
172 | | |
173 | | // If legacy decoding succeeded and passes CheckTxScriptsSanity, that's our answer, as we know |
174 | | // at this point that extended decoding either failed or doesn't pass the sanity check. |
175 | 0 | if (ok_legacy && CheckTxScriptsSanity(tx_legacy)) { |
176 | 0 | tx = std::move(tx_legacy); |
177 | 0 | return true; |
178 | 0 | } |
179 | | |
180 | | // If extended decoding succeeded, and neither decoding passes sanity, return the extended one. |
181 | 0 | if (ok_extended) { |
182 | 0 | tx = std::move(tx_extended); |
183 | 0 | return true; |
184 | 0 | } |
185 | | |
186 | | // If legacy decoding succeeded and extended didn't, return the legacy one. |
187 | 0 | if (ok_legacy) { |
188 | 0 | tx = std::move(tx_legacy); |
189 | 0 | return true; |
190 | 0 | } |
191 | | |
192 | | // If none succeeded, we failed. |
193 | 0 | return false; |
194 | 0 | } |
195 | | |
196 | | bool DecodeHexTx(CMutableTransaction& tx, const std::string& hex_tx, bool try_no_witness, bool try_witness) |
197 | 0 | { |
198 | 0 | if (!IsHex(hex_tx)) { |
199 | 0 | return false; |
200 | 0 | } |
201 | | |
202 | 0 | std::vector<unsigned char> txData(ParseHex(hex_tx)); |
203 | 0 | return DecodeTx(tx, txData, try_no_witness, try_witness); |
204 | 0 | } |
205 | | |
206 | | bool DecodeHexBlockHeader(CBlockHeader& header, const std::string& hex_header) |
207 | 0 | { |
208 | 0 | if (!IsHex(hex_header)) return false; |
209 | | |
210 | 0 | const std::vector<unsigned char> header_data{ParseHex(hex_header)}; |
211 | 0 | DataStream ser_header{header_data}; |
212 | 0 | try { |
213 | 0 | ser_header >> header; |
214 | 0 | } catch (const std::exception&) { |
215 | 0 | return false; |
216 | 0 | } |
217 | 0 | return true; |
218 | 0 | } |
219 | | |
220 | | bool DecodeHexBlk(CBlock& block, const std::string& strHexBlk) |
221 | 0 | { |
222 | 0 | if (!IsHex(strHexBlk)) |
223 | 0 | return false; |
224 | | |
225 | 0 | std::vector<unsigned char> blockData(ParseHex(strHexBlk)); |
226 | 0 | DataStream ssBlock(blockData); |
227 | 0 | try { |
228 | 0 | ssBlock >> TX_WITH_WITNESS(block); |
229 | 0 | } |
230 | 0 | catch (const std::exception&) { |
231 | 0 | return false; |
232 | 0 | } |
233 | | |
234 | 0 | return true; |
235 | 0 | } |
236 | | |
237 | | util::Result<int> SighashFromStr(const std::string& sighash) |
238 | 0 | { |
239 | 0 | static const std::map<std::string, int> map_sighash_values = { |
240 | 0 | {std::string("DEFAULT"), int(SIGHASH_DEFAULT)}, |
241 | 0 | {std::string("ALL"), int(SIGHASH_ALL)}, |
242 | 0 | {std::string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)}, |
243 | 0 | {std::string("NONE"), int(SIGHASH_NONE)}, |
244 | 0 | {std::string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)}, |
245 | 0 | {std::string("SINGLE"), int(SIGHASH_SINGLE)}, |
246 | 0 | {std::string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)}, |
247 | 0 | }; |
248 | 0 | const auto& it = map_sighash_values.find(sighash); |
249 | 0 | if (it != map_sighash_values.end()) { |
250 | 0 | return it->second; |
251 | 0 | } else { |
252 | 0 | return util::Error{Untranslated("'" + sighash + "' is not a valid sighash parameter.")}; |
253 | 0 | } |
254 | 0 | } |