Line data Source code
1 : // Copyright (c) 2018 The Bitcoin Core developers
2 : // Copyright (c) 2022 The PIVX 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 <script/descriptor.h>
7 :
8 : #include <key_io.h>
9 : #include <pubkey.h>
10 : #include <script/script.h>
11 : #include <script/standard.h>
12 :
13 : #include <span.h>
14 : #include <util/system.h>
15 : #include <utilstrencodings.h>
16 :
17 : #include <memory>
18 : #include <string>
19 : #include <vector>
20 :
21 : namespace {
22 :
23 : ////////////////////////////////////////////////////////////////////////////
24 : // Internal representation //
25 : ////////////////////////////////////////////////////////////////////////////
26 :
27 : typedef std::vector<uint32_t> KeyPath;
28 :
29 0 : std::string FormatKeyPath(const KeyPath& path)
30 : {
31 0 : std::string ret;
32 0 : for (auto i : path) {
33 0 : ret += strprintf("/%i", (i << 1) >> 1);
34 0 : if (i >> 31) ret += '\'';
35 : }
36 0 : return ret;
37 : }
38 :
39 : /** Interface for public key objects in descriptors. */
40 32 : struct PubkeyProvider
41 : {
42 0 : virtual ~PubkeyProvider() = default;
43 :
44 : /** Derive a public key. */
45 : virtual bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const = 0;
46 :
47 : /** Whether this represent multiple public keys at different positions. */
48 : virtual bool IsRange() const = 0;
49 :
50 : /** Get the size of the generated public key(s) in bytes (33 or 65). */
51 : virtual size_t GetSize() const = 0;
52 :
53 : /** Get the descriptor string form. */
54 : virtual std::string ToString() const = 0;
55 :
56 : /** Get the descriptor string form including private data (if available in arg). */
57 : virtual bool ToPrivateString(const SigningProvider& arg, std::string& out) const = 0;
58 : };
59 :
60 : /** An object representing a parsed constant public key in a descriptor. */
61 0 : class ConstPubkeyProvider final : public PubkeyProvider
62 : {
63 : CPubKey m_pubkey;
64 :
65 : public:
66 7 : explicit ConstPubkeyProvider(const CPubKey& pubkey) : m_pubkey(pubkey) {}
67 7 : bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override
68 : {
69 7 : out = m_pubkey;
70 7 : return true;
71 : }
72 7 : bool IsRange() const override { return false; }
73 0 : size_t GetSize() const override { return m_pubkey.size(); }
74 0 : std::string ToString() const override { return HexStr(m_pubkey); }
75 0 : bool ToPrivateString(const SigningProvider& arg, std::string& ret) const override
76 : {
77 0 : CKey key;
78 0 : if (!arg.GetKey(m_pubkey.GetID(), key)) return false;
79 0 : ret = KeyIO::EncodeSecret(key);
80 0 : return true;
81 : }
82 : };
83 :
84 : enum class DeriveType {
85 : NO,
86 : UNHARDENED,
87 : HARDENED,
88 : };
89 :
90 : /** An object representing a parsed extended public key in a descriptor. */
91 : class BIP32PubkeyProvider final : public PubkeyProvider
92 : {
93 : CExtPubKey m_extkey;
94 : KeyPath m_path;
95 : DeriveType m_derive;
96 :
97 9012 : bool GetExtKey(const SigningProvider& arg, CExtKey& ret) const
98 : {
99 18024 : CKey key;
100 9012 : if (!arg.GetKey(m_extkey.pubkey.GetID(), key)) return false;
101 9012 : ret.nDepth = m_extkey.nDepth;
102 9012 : std::copy(m_extkey.vchFingerprint, m_extkey.vchFingerprint + 4, ret.vchFingerprint);
103 9012 : ret.nChild = m_extkey.nChild;
104 9012 : ret.chaincode = m_extkey.chaincode;
105 9012 : ret.key = key;
106 : return true;
107 : }
108 :
109 15020 : bool IsHardened() const
110 : {
111 15020 : if (m_derive == DeriveType::HARDENED) return true;
112 21046 : for (auto entry : m_path) {
113 15038 : if (entry >> 31) return true;
114 : }
115 6008 : return false;
116 : }
117 :
118 : public:
119 25 : BIP32PubkeyProvider(const CExtPubKey& extkey, KeyPath path, DeriveType derive) : m_extkey(extkey), m_path(std::move(path)), m_derive(derive) {}
120 25 : bool IsRange() const override { return m_derive != DeriveType::NO; }
121 0 : size_t GetSize() const override { return 33; }
122 15020 : bool GetPubKey(int pos, const SigningProvider& arg, CPubKey& out) const override
123 : {
124 15020 : if (IsHardened()) {
125 18024 : CExtKey key;
126 9012 : if (!GetExtKey(arg, key)) return false;
127 27045 : for (auto entry : m_path) {
128 18033 : key.Derive(key, entry);
129 : }
130 9012 : if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos);
131 9012 : if (m_derive == DeriveType::HARDENED) key.Derive(key, pos | 0x80000000UL);
132 9012 : out = key.Neuter().pubkey;
133 : } else {
134 : // TODO: optimize by caching
135 6008 : CExtPubKey key = m_extkey;
136 18030 : for (auto entry : m_path) {
137 12022 : key.Derive(key, entry);
138 : }
139 6008 : if (m_derive == DeriveType::UNHARDENED) key.Derive(key, pos);
140 6008 : assert(m_derive != DeriveType::HARDENED);
141 6008 : out = key.pubkey;
142 : }
143 : return true;
144 : }
145 0 : std::string ToString() const override
146 : {
147 0 : std::string ret = KeyIO::EncodeExtPubKey(m_extkey) + FormatKeyPath(m_path);
148 0 : if (IsRange()) {
149 0 : ret += "/*";
150 0 : if (m_derive == DeriveType::HARDENED) ret += '\'';
151 : }
152 0 : return ret;
153 : }
154 0 : bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
155 : {
156 0 : CExtKey key;
157 0 : if (!GetExtKey(arg, key)) return false;
158 0 : out = KeyIO::EncodeExtKey(key) + FormatKeyPath(m_path);
159 0 : if (IsRange()) {
160 0 : out += "/*";
161 0 : if (m_derive == DeriveType::HARDENED) out += '\'';
162 : }
163 : return true;
164 : }
165 : };
166 :
167 : /** A parsed addr(A) descriptor. */
168 0 : class AddressDescriptor final : public Descriptor
169 : {
170 : CTxDestination m_destination;
171 :
172 : public:
173 5 : explicit AddressDescriptor(CTxDestination destination) : m_destination(std::move(destination)) {}
174 :
175 5 : bool IsRange() const override { return false; }
176 0 : std::string ToString() const override { return "addr(" + EncodeDestination(m_destination) + ")"; }
177 0 : bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
178 5 : bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
179 : {
180 15 : output_scripts = std::vector<CScript>{GetScriptForDestination(m_destination)};
181 5 : return true;
182 : }
183 : };
184 :
185 : /** A parsed raw(H) descriptor. */
186 : class RawDescriptor final : public Descriptor
187 : {
188 : CScript m_script;
189 :
190 : public:
191 0 : explicit RawDescriptor(CScript script) : m_script(std::move(script)) {}
192 :
193 0 : bool IsRange() const override { return false; }
194 0 : std::string ToString() const override { return "raw(" + HexStr(m_script) + ")"; }
195 0 : bool ToPrivateString(const SigningProvider& arg, std::string& out) const override { out = ToString(); return true; }
196 0 : bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
197 : {
198 0 : output_scripts = std::vector<CScript>{m_script};
199 0 : return true;
200 : }
201 : };
202 :
203 : /** A parsed pk(P), pkh(P), or wpkh(P) descriptor. */
204 0 : class SingleKeyDescriptor final : public Descriptor
205 : {
206 : const std::function<CScript(const CPubKey&)> m_script_fn;
207 : const std::string m_fn_name;
208 : std::unique_ptr<PubkeyProvider> m_provider;
209 :
210 : public:
211 6 : SingleKeyDescriptor(std::unique_ptr<PubkeyProvider> prov, const std::function<CScript(const CPubKey&)>& fn, const std::string& name) : m_script_fn(fn), m_fn_name(name), m_provider(std::move(prov)) {}
212 :
213 3 : bool IsRange() const override { return m_provider->IsRange(); }
214 0 : std::string ToString() const override { return m_fn_name + "(" + m_provider->ToString() + ")"; }
215 0 : bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
216 : {
217 0 : std::string ret;
218 0 : if (!m_provider->ToPrivateString(arg, ret)) return false;
219 0 : out = m_fn_name + "(" + std::move(ret) + ")";
220 0 : return true;
221 : }
222 3 : bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
223 : {
224 3 : CPubKey key;
225 3 : if (!m_provider->GetPubKey(pos, arg, key)) return false;
226 9 : output_scripts = std::vector<CScript>{m_script_fn(key)};
227 3 : out.pubkeys.emplace(key.GetID(), std::move(key));
228 3 : return true;
229 : }
230 : };
231 :
232 6 : CScript P2PKHGetScript(const CPubKey& pubkey) { return GetScriptForDestination(pubkey.GetID()); }
233 0 : CScript P2PKGetScript(const CPubKey& pubkey) { return GetScriptForRawPubKey(pubkey); }
234 :
235 : /** A parsed multi(...) descriptor. */
236 : class MultisigDescriptor : public Descriptor
237 : {
238 : int m_threshold;
239 : std::vector<std::unique_ptr<PubkeyProvider>> m_providers;
240 :
241 : public:
242 0 : MultisigDescriptor(int threshold, std::vector<std::unique_ptr<PubkeyProvider>> providers) : m_threshold(threshold), m_providers(std::move(providers)) {}
243 :
244 0 : bool IsRange() const override
245 : {
246 0 : for (const auto& p : m_providers) {
247 0 : if (p->IsRange()) return true;
248 : }
249 0 : return false;
250 : }
251 :
252 0 : std::string ToString() const override
253 : {
254 0 : std::string ret = strprintf("multi(%i", m_threshold);
255 0 : for (const auto& p : m_providers) {
256 0 : ret += "," + p->ToString();
257 : }
258 0 : return std::move(ret) + ")";
259 : }
260 :
261 0 : bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
262 : {
263 0 : std::string ret = strprintf("multi(%i", m_threshold);
264 0 : for (const auto& p : m_providers) {
265 0 : std::string sub;
266 0 : if (!p->ToPrivateString(arg, sub)) return false;
267 0 : ret += "," + std::move(sub);
268 : }
269 0 : out = std::move(ret) + ")";
270 0 : return true;
271 : }
272 :
273 0 : bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
274 : {
275 0 : std::vector<CPubKey> pubkeys;
276 0 : pubkeys.reserve(m_providers.size());
277 0 : for (const auto& p : m_providers) {
278 0 : CPubKey key;
279 0 : if (!p->GetPubKey(pos, arg, key)) return false;
280 0 : pubkeys.push_back(key);
281 : }
282 0 : for (const CPubKey& key : pubkeys) {
283 0 : out.pubkeys.emplace(key.GetID(), std::move(key));
284 : }
285 0 : output_scripts = std::vector<CScript>{GetScriptForMultisig(m_threshold, pubkeys)};
286 0 : return true;
287 : }
288 : };
289 :
290 : /** A parsed sh(S) or wsh(S) descriptor. */
291 : class ConvertorDescriptor : public Descriptor
292 : {
293 : const std::function<CScript(const CScript&)> m_convert_fn;
294 : const std::string m_fn_name;
295 : std::unique_ptr<Descriptor> m_descriptor;
296 :
297 : public:
298 0 : ConvertorDescriptor(std::unique_ptr<Descriptor> descriptor, const std::function<CScript(const CScript&)>& fn, const std::string& name) : m_convert_fn(fn), m_fn_name(name), m_descriptor(std::move(descriptor)) {}
299 :
300 0 : bool IsRange() const override { return m_descriptor->IsRange(); }
301 0 : std::string ToString() const override { return m_fn_name + "(" + m_descriptor->ToString() + ")"; }
302 0 : bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
303 : {
304 0 : std::string ret;
305 0 : if (!m_descriptor->ToPrivateString(arg, ret)) return false;
306 0 : out = m_fn_name + "(" + std::move(ret) + ")";
307 0 : return true;
308 : }
309 0 : bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
310 : {
311 0 : std::vector<CScript> sub;
312 0 : if (!m_descriptor->Expand(pos, arg, sub, out)) return false;
313 0 : output_scripts.clear();
314 0 : for (const auto& script : sub) {
315 0 : CScriptID id(script);
316 0 : out.scripts.emplace(CScriptID(script), script);
317 0 : output_scripts.push_back(m_convert_fn(script));
318 : }
319 0 : return true;
320 : }
321 : };
322 :
323 0 : CScript ConvertP2SH(const CScript& script) { return GetScriptForDestination(CScriptID(script)); }
324 :
325 : /** A parsed combo(P) descriptor. */
326 : class ComboDescriptor final : public Descriptor
327 : {
328 : std::unique_ptr<PubkeyProvider> m_provider;
329 :
330 : public:
331 29 : explicit ComboDescriptor(std::unique_ptr<PubkeyProvider> provider) : m_provider(std::move(provider)) {}
332 :
333 29 : bool IsRange() const override { return m_provider->IsRange(); }
334 0 : std::string ToString() const override { return "combo(" + m_provider->ToString() + ")"; }
335 0 : bool ToPrivateString(const SigningProvider& arg, std::string& out) const override
336 : {
337 0 : std::string ret;
338 0 : if (!m_provider->ToPrivateString(arg, ret)) return false;
339 0 : out = "combo(" + std::move(ret) + ")";
340 0 : return true;
341 : }
342 15024 : bool Expand(int pos, const SigningProvider& arg, std::vector<CScript>& output_scripts, FlatSigningProvider& out) const override
343 : {
344 15024 : CPubKey key;
345 15024 : if (!m_provider->GetPubKey(pos, arg, key)) return false;
346 15024 : CKeyID keyid = key.GetID();
347 15024 : {
348 15024 : CScript p2pk = GetScriptForRawPubKey(key);
349 30048 : CScript p2pkh = GetScriptForDestination(keyid);
350 60096 : output_scripts = std::vector<CScript>{std::move(p2pk), std::move(p2pkh)};
351 15024 : out.pubkeys.emplace(keyid, key);
352 : }
353 15024 : return true;
354 : }
355 : };
356 :
357 : ////////////////////////////////////////////////////////////////////////////
358 : // Parser //
359 : ////////////////////////////////////////////////////////////////////////////
360 :
361 : enum class ParseScriptContext {
362 : TOP,
363 : P2SH,
364 : };
365 :
366 : /** Parse a constant. If successful, sp is updated to skip the constant and return true. */
367 0 : bool Const(const std::string& str, Span<const char>& sp)
368 : {
369 0 : if ((size_t)sp.size() >= str.size() && std::equal(str.begin(), str.end(), sp.begin())) {
370 0 : sp = sp.subspan(str.size());
371 0 : return true;
372 : }
373 : return false;
374 : }
375 :
376 : /** Parse a function call. If successful, sp is updated to be the function's argument(s). */
377 123 : bool Func(const std::string& str, Span<const char>& sp)
378 : {
379 123 : if ((size_t)sp.size() >= str.size() + 2 && sp[str.size()] == '(' && sp[sp.size() - 1] == ')' && std::equal(str.begin(), str.end(), sp.begin())) {
380 37 : sp = sp.subspan(str.size() + 1, sp.size() - str.size() - 2);
381 37 : return true;
382 : }
383 : return false;
384 : }
385 :
386 : /** Return the expression that sp begins with, and update sp to skip it. */
387 37 : Span<const char> Expr(Span<const char>& sp)
388 : {
389 37 : int level = 0;
390 37 : auto it = sp.begin();
391 3912 : while (it != sp.end()) {
392 3875 : if (*it == '(') {
393 37 : ++level;
394 3838 : } else if (level && *it == ')') {
395 37 : --level;
396 3801 : } else if (level == 0 && (*it == ')' || *it == ',')) {
397 : break;
398 : }
399 3875 : ++it;
400 : }
401 37 : Span<const char> ret = sp.first(it - sp.begin());
402 37 : sp = sp.subspan(it - sp.begin());
403 37 : return ret;
404 : }
405 :
406 : /** Split a string on every instance of sep, returning a vector. */
407 32 : std::vector<Span<const char>> Split(const Span<const char>& sp, char sep)
408 : {
409 32 : std::vector<Span<const char>> ret;
410 32 : auto it = sp.begin();
411 32 : auto start = it;
412 3489 : while (it != sp.end()) {
413 3457 : if (*it == sep) {
414 75 : ret.emplace_back(start, it);
415 75 : start = it + 1;
416 : }
417 3457 : ++it;
418 : }
419 32 : ret.emplace_back(start, it);
420 32 : return ret;
421 : }
422 :
423 : /** Parse a key path, being passed a split list of elements (the first element is ignored). */
424 25 : bool ParseKeyPath(const std::vector<Span<const char>>& split, KeyPath& out)
425 : {
426 90 : for (size_t i = 1; i < split.size(); ++i) {
427 65 : Span<const char> elem = split[i];
428 65 : bool hardened = false;
429 65 : if (elem.size() > 0 && (elem[elem.size() - 1] == '\'' || elem[elem.size() - 1] == 'h')) {
430 26 : elem = elem.first(elem.size() - 1);
431 26 : hardened = true;
432 : }
433 65 : uint32_t p;
434 195 : if (!ParseUInt32(std::string(elem.begin(), elem.end()), &p) || p > 0x7FFFFFFFUL) return false;
435 65 : out.push_back(p | (((uint32_t)hardened) << 31));
436 : }
437 : return true;
438 : }
439 :
440 32 : std::unique_ptr<PubkeyProvider> ParsePubkey(const Span<const char>& sp, bool permit_uncompressed, FlatSigningProvider& out)
441 : {
442 64 : auto split = Split(sp, '/');
443 96 : std::string str(split[0].begin(), split[0].end());
444 32 : if (split.size() == 1) {
445 7 : if (IsHex(str)) {
446 7 : std::vector<unsigned char> data = ParseHex(str);
447 7 : CPubKey pubkey(data);
448 7 : if (pubkey.IsFullyValid() && (permit_uncompressed || pubkey.IsCompressed())) return std::make_unique<ConstPubkeyProvider>(pubkey);
449 : }
450 0 : CKey key = KeyIO::DecodeSecret(str);
451 0 : if (key.IsValid() && (permit_uncompressed || key.IsCompressed())) {
452 0 : CPubKey pubkey = key.GetPubKey();
453 0 : out.keys.emplace(pubkey.GetID(), key);
454 0 : return std::make_unique<ConstPubkeyProvider>(pubkey);
455 : }
456 : }
457 57 : CExtKey extkey = KeyIO::DecodeExtKey(str);
458 25 : CExtPubKey extpubkey = KeyIO::DecodeExtPubKey(str);
459 25 : if (!extkey.key.IsValid() && !extpubkey.pubkey.IsValid()) return nullptr;
460 50 : KeyPath path;
461 25 : DeriveType type = DeriveType::NO;
462 25 : if (split.back() == MakeSpan("*").first(1)) {
463 6 : split.pop_back();
464 6 : type = DeriveType::UNHARDENED;
465 19 : } else if (split.back() == MakeSpan("*'").first(2) || split.back() == MakeSpan("*h").first(2)) {
466 4 : split.pop_back();
467 4 : type = DeriveType::HARDENED;
468 : }
469 50 : if (!ParseKeyPath(split, path)) return nullptr;
470 25 : if (extkey.key.IsValid()) {
471 20 : extpubkey = extkey.Neuter();
472 40 : out.keys.emplace(extpubkey.pubkey.GetID(), extkey.key);
473 : }
474 25 : return std::make_unique<BIP32PubkeyProvider>(extpubkey, std::move(path), type);
475 : }
476 :
477 : /** Parse a script in a particular context. */
478 37 : std::unique_ptr<Descriptor> ParseScript(Span<const char>& sp, ParseScriptContext ctx, FlatSigningProvider& out)
479 : {
480 37 : auto expr = Expr(sp);
481 37 : if (Func("pk", expr)) {
482 0 : auto pubkey = ParsePubkey(expr, true, out);
483 0 : if (!pubkey) return nullptr;
484 0 : return std::make_unique<SingleKeyDescriptor>(std::move(pubkey), P2PKGetScript, "pk");
485 : }
486 37 : if (Func("pkh", expr)) {
487 6 : auto pubkey = ParsePubkey(expr, true, out);
488 6 : if (!pubkey) return nullptr;
489 3 : return std::make_unique<SingleKeyDescriptor>(std::move(pubkey), P2PKHGetScript, "pkh");
490 : }
491 73 : if (ctx == ParseScriptContext::TOP && Func("combo", expr)) {
492 58 : auto pubkey = ParsePubkey(expr, true, out);
493 58 : if (!pubkey) return nullptr;
494 29 : return std::make_unique<ComboDescriptor>(std::move(pubkey));
495 : }
496 5 : if (Func("multi", expr)) {
497 0 : auto threshold = Expr(expr);
498 0 : uint32_t thres;
499 0 : std::vector<std::unique_ptr<PubkeyProvider>> providers;
500 0 : if (!ParseUInt32(std::string(threshold.begin(), threshold.end()), &thres)) return nullptr;
501 : size_t script_size = 0;
502 0 : while (expr.size()) {
503 0 : if (!Const(",", expr)) return nullptr;
504 0 : auto arg = Expr(expr);
505 0 : auto pk = ParsePubkey(arg, true, out);
506 0 : if (!pk) return nullptr;
507 0 : script_size += pk->GetSize() + 1;
508 0 : providers.emplace_back(std::move(pk));
509 : }
510 0 : if (providers.size() < 1 || providers.size() > 16 || thres < 1 || thres > providers.size()) return nullptr;
511 0 : if (ctx == ParseScriptContext::TOP) {
512 0 : if (providers.size() > 3) return nullptr; // Not more than 3 pubkeys for raw multisig
513 : }
514 0 : if (ctx == ParseScriptContext::P2SH) {
515 0 : if (script_size + 3 > 520) return nullptr; // Enforce P2SH script size limit
516 : }
517 0 : return std::make_unique<MultisigDescriptor>(thres, std::move(providers));
518 : }
519 15 : if (ctx == ParseScriptContext::TOP && Func("sh", expr)) {
520 0 : auto desc = ParseScript(expr, ParseScriptContext::P2SH, out);
521 0 : if (!desc || expr.size()) return nullptr;
522 0 : return std::make_unique<ConvertorDescriptor>(std::move(desc), ConvertP2SH, "sh");
523 : }
524 10 : if (ctx == ParseScriptContext::TOP && Func("addr", expr)) {
525 15 : CTxDestination dest = DecodeDestination(std::string(expr.begin(), expr.end()));
526 10 : if (!IsValidDestination(dest)) return nullptr;
527 5 : return std::make_unique<AddressDescriptor>(std::move(dest));
528 : }
529 0 : if (ctx == ParseScriptContext::TOP && Func("raw", expr)) {
530 0 : std::string str(expr.begin(), expr.end());
531 0 : if (!IsHex(str)) return nullptr;
532 0 : auto bytes = ParseHex(str);
533 0 : return std::make_unique<RawDescriptor>(CScript(bytes.begin(), bytes.end()));
534 : }
535 37 : return nullptr;
536 : }
537 :
538 : } // namespace
539 :
540 37 : std::unique_ptr<Descriptor> Parse(const std::string& descriptor, FlatSigningProvider& out)
541 : {
542 37 : Span<const char> sp(descriptor.data(), descriptor.size());
543 37 : auto ret = ParseScript(sp, ParseScriptContext::TOP, out);
544 37 : if (sp.size() == 0 && ret) return ret;
545 37 : return nullptr;
546 : }
|